位操作
如何实现位操作求两个数的平均值
一般而言,求解平均值的方法就是两数相加除以2,但是采用这种方法会存在一个问题,当两个数比较大时,如两者的和大于机器位数能够表示的最大值,可能会存在数据溢出的情况,而采用位运算的方法则可以避免这一问题。
代码示例:
#include <stdio.h>
int main()
{
int x = 2147483647;
int y = 2147483647;
printf("%d\n",(x+y)/2);//存在数据溢出的情况
printf("%d\n",(x&y)+((x^y)>>1));
return 0;
}
如何利用位运算计算数的绝对值
以x为负数为例来分析。因为在计算机中,数字都是以补码的形式存放的,求负数的绝对值,就应该不管符号位,执行按位取反,末位加1操作。
对于一个负数,将其右移31位后变成0xffffffff,而对于一个整数而言,右移31位则为0x00000000,而0xffffffff^x+x=-1,因为1011^1111=0100,任何数与1111异或,其实质都是把x的0和1进行颠倒计算。如果用变量y表示x右移31位,(x^y)-y则表示的是x的绝对值。
代码示例:
#include <stdio.h>
int MyAbs(int x)
{
int y;
y = x >> 31;
return (x^y)-y; //此处还可以写为(x+y)^y
}
int main()
{
printf("%d\n",MyAbs(2));
printf("%d\n",MyAbs(-2));
return 0;
}
如何求解整型数的二进制表示中1的个数
求解整型数的二进制表示中1的个数有以下两种方法
代码示例:
#include <stdio.h>
//方法1
int func1(int x)
{
int countx = 0;
while(x)
{
countx++;
x = x & (x-1);
}
return countx;
}
//方法2
int func2(unsigned int n)
{
int count = 0;
while(n)
{
count += n & 0x1u; //0x1u表示十六进制的无符号数1
n >>= 1;
}
return count;
}
int main()
{
printf("func1:%d\n",func1(9999));
printf("func2:%d\n",func2(9999));
return 0;
}
理解方法1中的操作:
1)当一个数被减1时,它最右边的那个值为1的bit将变成0,同时其右边的所有的bit都会变成1;
2)“&=”,位与并赋值操作。去掉已经被计数过的1,并将该值重新设置给n.这个算法循环的次数是bit位为1的个数。也就是说,有几个bit为1,循环几次,对bit为1比较稀疏的数来说,性能很好。
方法2判断每个数的二进制表示中每一位是否为1,如果为1,就在count上加1,而循环的次数是常数,即n的位数。该方法缺陷是在1比较稀疏的时候效率会比较低。
不能用sizeof()函数,如何判断操作系统是16位还是32位
代码示例:
#include <stdio.h>
//方法1
int main()
{
int i = 65536;
printf("%d\n",i);
int j = 65535;
printf("%d\n",j);
return 0;
}
//16位机器输出为:0 -1
//32位机器输出为:65536 65535
#if 0
int main()
{
unsigned int a = ~0;
if(a > 65536)
{
printf("32位\n");
}
else
{
printf("16位\n");
}
return 0;
}
#endif
方法1:一般而言,机器位数不同,其表示的数字的最大值也不同,根据这一特性,可以判断操作系统位数。之所以有区别,是因为在16位操作系统下,能够表示的最大数为65535,所以会存在最高位溢出的情况。当变量的值为65535时,输出为0;当变量值为65535时,输出为-1.而在32位机器上,则不会出现溢出的情况,所以正常输出。
方法2:对0值取反,不同位数下的0值取反,其结果也不一样。例如,在32位机器下,按位取反运算,其结果为11111111111111111111111111111111.
如何判断计算机处理器是大端还是小端?
#include <stdio.h>
int main()
{
union checkcpu
{
int a;
char ch;
};
checkcpu CPU;
CPU.a = 0x12345678;
if(CPU.ch == 0x78)
{
printf("小端字节序\n");
}
else
{
printf("大端字节序\n");
}
return 0;
}
考虑n位二进制数,有多少个数中不存在相邻的1?
当n=1时,满足条件的二进制数为0、1,一共两个数;当n=2时,满足条件的二进制数有00、01、10,一共3个数;当n=3时,满足条件的二进制数有000,001,010,100,101,一共5个数。对于n位二进制,设所求结果为a(n),对于第n位的值,分为0或者1两种情况:
1)第n位为0,则有a(n-1)个数
2)第n位为1,则要满足没有相邻为1的条件,第n-1位为0,有a(n-2)个数,因此得到结论a(n)=a(n-1)+a(n-2)
通过观察发现,满足斐波拉契数列,因此代码示例:
//考虑n位二进制数,有多少个数中不存在两个相邻的1
#include <stdio.h>
long Fibonacci(int i)
{
if(i == 1 || i ==2)
{
return 1;
}
else
{
return (Fibonacci(i-1) + Fibonacci(i-2));
}
}
int main()
{
printf("%ld\n",Fibonacci(7));
return 0;
}
不用除法操作符如何实现两个正整数的除法?
//不用除法操作符如何实现两个正整数额除法
#include <stdio.h>
//方法1:可以根据除法运算的原理进行减法操作,对除数循环减被除数,减一次结果加1,直到刚好减为0,或者余数小于被除数为止。
int MyDiv1(int a,int b)
{
int result;
if(b == 0)
{
printf("除数不能为0\n");
return result;
}
while(a >= b)
{
result++;
a = a-b;
}
return result;
}
//方法2:递归法求解。采用比较数翻倍的比较方法,算法效率将得到极大优化
int MyDiv2(int a,int b)
{
int k = 0;
int c = b;
int res = 0;
if(b == 0)
{
printf("除数不能为0\n");
}
if(a < b)
{
return 0;
}
for(; a>=c; c <<= 1,k++)
{
if(a - c < b)
{
return 1 << k;
}
}
return MyDiv2(a-(c>>1),b)+(1 << (k-1));
}
//方法3:采用移位操作实现,位操作效率一般都比较高效
int MyDiv3(int x,int y)
{
int left_num = x;
int result = 0;
while(left_num >= y)
{
int multi = 1;
while(y * multi <= (left_num >> 1))
{
multi = multi << 1;
}
result += multi;
left_num -= y * multi;
}
return result;
}
int main()
{
printf("%d\n",MyDiv1(10,3));
printf("%d\n",MyDiv2(10,3));
printf("%d\n",MyDiv3(10,3));
return 0;
}
不用加法操作符如何实现两个正整数的加法?
#include<stdio.h>
int add1(int num1,int num2)
{
if(0 == num2)
{
return num1;
}
int sumTemp = num1 ^ num2;
int carry = (num1 & num2) << 1;
return add1(sumTemp,carry);
}
int add2(int num1,int num2)
{
int sum = 0;
int num3 = 0;
int num4 = 0;
while((num1 & num2) > 0)
{
num3 = num1 ^ num2;
num4 = num1 & num2;
num1 = num3;
num2 = num4 << 1;
}
sum = num1 ^ num2;
return sum;
}
int main()
{
printf("%d\n",add1(100,200));
printf("%d\n",add2(100,200));
return 0;
}
不用乘法操作符如何实现两个正整数的乘法?
#include <iostream>
#include <map>
using namespace std;
int mul(int a, int b)
{
bool neg = (b < 0);
if(b < 0)
{
b = -b;
}
int sum = 0;
map<int,int>bit_map;
for(int i = 0; i < 32; i++)
{
bit_map.insert(pair<int,int>(1 << i,i));
}
while(b > 0)
{
int last_bit = bit_map[b & ~(b-1)];
sum += (a << last_bit);
b &= b -1;
}
if(neg)
{
sum = -sum;
}
return sum;
}
int main()
{
printf("%d\n",mul(3,5));
return 0;
}