位操作
位操作基础
|
符号 |
描述 |
运算规则 by MoreWindows |
|
& |
与 |
两个位都为1时,结果才为1 |
|
| |
或 |
两个位都为0时,结果才为0 |
|
^ |
异或 |
两个位相同为0,相异为1 |
|
~ |
取反 |
0变1,1变0 |
|
<< |
左移 |
各二进位全部左移若干位,高位丢弃,低位补0 |
|
>> |
右移 |
各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
常用位操作小技巧
判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。
// 下面程序将输出0到100之间的所有奇数。
private void Start()
{
for (int i = 0; i < 100; i++)
{
if(((i&1)==0?false:true))
{
Console.WriteLine(i);
}
}
}
交换两数
一般的写法是:
private void Swap(ref int a,ref int b)
{
int c=a;
a=b;
b=c;
}
用位操作来实现交换两数而不用第三方变量:
private void Swap(ref int a,ref int b)
{
a^=b;
b^=a;
a^=b;
}
变换符号
如对于-11和11,可以通过下面的变换方法将-11变成11
1111 0101(二进制) –取反-> 0000 1010(二进制) –加1-> 0000 1011(二进制)
同样可以这样的将11变成-11
0000 1011(二进制) –取反-> 0000 0100(二进制) –加1-> 1111 0101(二进制)
因此变换符号只需要取反后加1即可。完整代码如下:
private static int SignReversal(int a)
{
return ~a + 1;
}
求绝对值
位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。对-6可以这样:
1111 1010(二进制) –取反->0000 0101(二进制) -加1-> 0000 0110(二进制)来得到6。
因此先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。完整代码如下:
private int ABS(int num)
{
int i = num >> 31;
return i == 0 ? num : (~num + 1);
}
现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。所以可以对上面代码优化下:
int my_abs(int a)
{
int i = a >> 31;
return ((a ^ i) - i);
}
求平均数
求平均数 (x+y)/2 这样吗?考虑过 x+y可能超过int的范围吗?正确的姿势是 (x&y)+((x^y)>>1)
// x+y = 奇数,例137,该算法 = 68; x+y = 偶数,例136,该算法 = 68;
private int average(int x,int y)
{
return (x&y)+((x^y)>>1);
}
二进制补码运算公式
-x = ~x + 1 = ~(x-1)
~x = -x-1
-(~x) = x+1
~(-x) = x-1
x+y = x - ~y - 1 = (x|y)+(x&y)
x-y = x + ~y + 1 = (x|~y)-(~x&y)
x^y = (x|y)-(x&y)
x|y = (x&~y)+y
x&y = (~x|y)-~x
x==y: ~(x-y|y-x)
x!=y: x-y|y-x
x< y: (x-y)^((x^y)&((x-y)^x))
x<=y: (x|~y)&((x^y)|~(y-x))
x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y比较
x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y比较

浙公网安备 33010602011771号