--------------------------------------------------------------------------------------------------------------------------------------------------------------
位操作,其实就是直接在内存中对二进制位进行操作,由于CPU支持位操作,因此位操作的效率是很高的,但是位操作只能进行2的倍数的操作。下面介绍位运算的一些小技巧。
-------------------------------------------------------------------------------
一直以来,对位操作比较迷糊,也一直想了解,所以今天在网上查了些资料,发现一篇bithacks写的不错,因此节选翻译给大家!第一次翻译!英文有点烂!大家见谅!
此篇文章节选翻译自 http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/
1、测试给定数字的奇偶性
if ((x & 1) == 0) {
x is even
} else {
x is odd
}
判断一个数是不是奇数,只需要判断该数的二进制表示的末尾是不是1即可。
通过举个简单的例子,给大家讲解下!
00101011
& 00000001 (note: 1 is the same as 00000001)
--------
00000001
通过上面的例子可以看到,将给定数字与1进行 ‘与运算’如果结果为1则是奇数,否则为偶数。
2、测试给定数字的第n位是否是1
if (x & (1<<n)) {
n-th bit is set
} else {
n-th bit is not set
}
在第一个例子中,我们通过 x&1 来判断第一个二进制位是否是1.本条技巧扩展了技巧1的结果。
通过将1向左移动n位然后与判定数字进行与运算来判断该位置是否置1.
1 00000001 (same as 1<<0)
1<<1 00000010
1<<2 00000100
1<<3 00001000
1<<4 00010000
1<<5 00100000
1<<6 01000000
1<<7 10000000
eg.122的第三位置1了吗?
122 & (1<<3)
其中,122的二进制表示为:01111010 &
1<<3 的二进制表示为:00001000
------------
00001000
结果不为0,因此,122的二进制表示的第三位是1.
3、将给定数字的第n位置1
y = x | (1<<n)
这是由于 1 or 0 或者 1 or 1 结果该位都会被置1.
假设给定数字为120,我们希望把第二位打开。
其中,120的二进制表示为: 01111000 (120 的二进制表示)
| 00000100 (1<<2)
--------
01111100
这个很简单吧!
4、将给定数字的第n位置0
y = x & ~(1<<n)
此种情况下一个重要的技巧是:~(1<<n),它把除了第n位的其他位都置1了。
看起来是下面的这个样子:
~1 11111110 (same as ~(1<<0))
~(1<<1) 11111101
~(1<<2) 11111011
~(1<<3) 11110111
~(1<<4) 11101111
~(1<<5) 11011111
~(1<<6) 10111111
~(1<<7) 01111111
而且,0& what 都等于0.因此,就关闭了第n位,而不会对其它位造成影响。
下面看一个例子,将127的第四位置0.
01111111 (127 in binary)
& 11101111 (~(1<<4))
--------
01101111
5、将给定数字的第n位的数字转换(如是0,转成1;如是1,转成0)
看到上面的讲解大家应该知道用异或运算了。
y = x ^ (1<<n)
下面看个具体的例子:假设想要把01110101的第五位转换下。
01110101
^ 00100000
--------
01010101
假设是这个数字的第五位呢, 01010101
01010101
^ 00100000
--------
01110101
发现什么规律没有,异或两次会返回同样的值,异或这种精致的技巧被用来计算RAID 阵列的均衡性,但这都是在其他论文里面才有的。
6、将给定数字的最右边的1置0
y = x & (x-1)
这个技巧将最右边的1-bit 置0。举个例子,给定整数:00101010(最右边的1用黑体标识了),
经过转换后变成了00101000
下面再给几个例子:
01010111 (x)
& 01010110 (x-1)
--------
01010110 01011000 (x)
& 01010111 (x-1)
--------
01010000 10000000 (x = -128)
& 01111111 (x-1 = 127 (with overflow))
--------
00000000 11111111 (x = all bits 1)
& 11111110 (x-1)
--------
11111110 00000000 (x = no rightmost 1-bits)
& 11111111 (x-1)
--------
00000000
为什么能达到这个效果呢?
如果你仔细思考一会,你应该能想到,会出现以下两种情况:
1)给定数字最右边有1.在这种情况下,将该数减1,会将原数中所有的0位置1,并且将最右边的1置0(想想为什么?).
然后,进行与运算,将最右边的1置0.
因此,如果你将转换后的数字加上1,那么你就得到了原来的数字。
2)给定数字最右边没有1(all 0).此种情况下,再减去1的话,会将所有的为置1,因此进行与运算的时候又得到了原来的数。
7、除了最右边的1,其他位置0
y = x & (-x)
这个位操作技巧能够找到最右边的1,并且将其他位置0。
例如, 01010100(最右边的1用黑体标识)
被转换成 00000100
看下面更多的例子:
10111100 (x)
& 01000100 (-x)
--------
00000100 01110000 (x)
& 10010000 (-x)
--------
00010000 00000001 (x)
& 11111111 (-x)
--------
00000001 10000000 (x = -128)
& 10000000 (-x = -128)
--------
10000000 11111111 (x = all bits one)
& 00000001 (-x)
--------
00000001 00000000 (x = all bits 0, no rightmost 1-bit)
& 00000000 (-x)
--------
00000000
这个技巧能够起作用是因为:-x 和 ~x+1 是相等的
.
8、将最右边的 1的 右边位置 置0
y = x | (x-1)
这个技巧最好通过例子来讲解,比如,给定的值是01010000,将会被转换成01011111.所有的从右边到最右边1的位被置1.
这是一个不太精致的技巧,它会将0的所有位置1.
让我们来看更多的例子:
10111100 (x)
| 10111011 (x-1)
--------
10111111 01110111 (x)
| 01110110 (x-1)
--------
01110111 00000001 (x)
| 00000000 (x-1)
--------
00000001 10000000 (x = -128)
| 01111111 (x-1 = 127)
--------
11111111 11111111 (x = -1)
| 11111110 (x-1 = -2)
--------
11111111 00000000 (x)
| 11111111 (x-1)
--------
11111111
让我们来证明一下:
也是有两种情况,
1)没有最右边的1,这种情况下x = 0,x-1 = -1,-1的二进制表示为11111111,或运算会将所有的位置1,尽管不是想要的结果,但情况确实是这样。
2)有最右边的1.让我们把这个数分成两部分,x-1会将修改右边的位,将有1的置为0,将低位置1,然后 x | (x-1),让所有1保留,让最右边的1保留,并且将最右边的1后面的位置1.(这点翻译的不太好,建议看原文:There is the rightmost 1-bit bi. Let's divide all the bits in two groups again (like in the previous example). Calculating x-1 modifies only bits to the right, turning bi into 0, and all the lower bits to 1's. Now OR-ing x with x-1 leaves all the higher bits (to the left) the same, leaves bit bi as it was 1, and since lower bits are all low 1's it also turns them on. The result is that the rightmost 1-bit got propagated to lower order bits.)
9、此技巧与技巧7正好相反,
找到最右边的0,将它置1,所以其他位置0
y = ~x & (x+1)
下面看一些例子:
10111100 (x)
--------
01000011 (~x)
& 10111101 (x+1)
--------
00000001 01110111 (x)
--------
10001000 (~x)
& 01111000 (x+1)
--------
00001000 00000001 (x)
--------
11111110 (~x)
& 00000010 (x+1)
--------
00000010 10000000 (x = -128)
--------
01111111 (~x)
& 10000001 (x+1)
--------
00000001 11111111 (x = no rightmost 0-bit)
--------
00000000 (~x)
& 00000000 (x+1)
--------
00000000 00000000 (x)
--------
11111111 (~x)
& 00000001 (x+1)
--------
00000001
证明:假设有一个最右边的0,因此,~x,将最右边的0置1,然后x+1会将最右边的0置1,然后与运算后除最右边的0变成1,所有其他位被置0了。
9、将最右边的0置1
y = x | (x+1)
例如:给定数字为10100011
转换后为10100111
更多的例子:
10111100 (x)
| 10111101 (x+1)
--------
10111101 01110111 (x)
| 01111000 (x+1)
--------
01111111 00000001 (x)
| 00000010 (x+1)
--------
00000011 10000000 (x = -128)
| 10000001 (x+1)
--------
10000001 11111111 (x = no rightmost 0-bit)
| 00000000 (x+1)
--------
11111111 00000000 (x)
| 00000001 (x+1)
--------
00000001
证明:将x和x+1进行或运算不会丢失任何信息。将x加1后,填充了最右边的第一个0。结果是max(x,x+1).如果x+1 溢出的话,那就没有0位了。如果不溢出,结果就会把最右边的第一个0置为1.
最后作者还提供了一个将一个十进制的整数转换成2进制的小程序:
void int_to_bin(int num) {
char str[9] = {0};
int i;
for (i=7; i>=0; i--) {
str[i] = (num&1)?'1':'0';
num >>= 1;
}
printf("%s\n", str);
}
-----------------------------------------------------------------------------------------------------------------
翻译完成,希望能够帮助到你!
转载请注明!链接:http://blog.csdn.net/luozhaowork/article/details/10362905
Thank any way!