【这里我们将讨论一些位操作技巧,如果使用得当会有助于提高你编出的程序执行速度】
我们编程或多或少会使用到位操作,位操作是编程中处理整型数据高效方法。例如,想要求出一个整数二进制位中值为 1 的个数,简单的方法是用一个循环,其实我们用一两个位操作的小技巧就能高效的解决。
假设读者已经知道整型数据二进制补码表示方式和位操作的基本方式。
我列出下文中将要使用的位操作标记:
& - bitwise and | - bitwise or ^ - bitwise xor ~ - bitwise not << - bitwise shift left >> - bitwise shift right
本文中使用的数字是8bit无符号整型,二进制补码表示,用‘x’标记,结果用‘y’表示。数‘x’的每一比特位标记为b7, b6, b5, b4, b3, b3, b2, b1 和 b0。b7 是符号位,权重最大,b0 权重最小。
我们从最简单的开始逐渐过渡到难的。
技巧1:检查整数是奇数还是偶数
if ((x & 1) == 0) { // x is even } else { // x is odd }
相信很多人都知道这个方法,只要整数的最后一位比特是 1 ,那它就是奇数,反之就是偶数。即 b0 位要么是 1 要么是 0 ,‘x’和 1 与(&)运算,保留了最 b0 位,如果 b0 是 1 ,‘x’是奇数,如果 b0 位是 0 ,‘x’是偶数。
如果还不是很清楚我们去一个整数,例如43,二进制表示为00101011,注意 b0 位为 1,我们将 43 与 1 做 & 运算:
1 00101011 2& 00000001 (note: 1 is the same as 00000001) 3 -------- 4 00000001
从中看到 & 运算怎样擦除了高位的 b7 ~ b1 位的数据只保留了 b0 位,结果为 1,因此知道 43 是奇数。
技巧2:测试第n位比特
if (x & (1<<n)) { // n-th bit is set } else { // n-th bit is not set }
前面讨论过了第一位比特测试,这里做了一些改进,可以测试第任意位比特,只要将与运算的1向左平移相应的位数即可。假设向左平移n位,接下来的与运算就是只保留第n位,其它位都清零了。
下面是简单的演示:
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
小例子:
122的第三位比特是1吗?(从0开始数)可以这样做:
122 & (1<<3)
122的二进制表示是01111010,(1<<3)即1向左平移3比特00001000。
1 01111010 2& 00001000 3 -------- 4 00001000
我们看到结果不为0,这题的答案是1。
技巧3:将第n位设为1
y = x | (1<<n)
和前面的技巧一样,只是把与运算(&)换成了或运算(|)。与1进行或运算将参与运算的位置为1,与0进行或运算参与预算的位不变。
看一个例子:
假设我们要将120的第2位比特设为1:
1 01111000 (120 in binary) 2| 00000100 (1<<2) 3 -------- 4 01111100
技巧4:将第n位设为0
y = x & ~(1<<n)
这个方法的关键就是~(1<<n),它将第n位设为0,其它位全部为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
与‘x’与运算的结果是清零了第n位,不管这一位是1还是0,其它位保持不变。
小例子,将127的第4位设为0:
1 01111111 (127 in binary) 2& 11101111 (~(1<<4)) 3 -------- 4 01101111
技巧5:将第n位取反
y = x ^ (1<<n)
这次使用的是异或运算,如果异或运算的两个操作数相同,运算结果是0,两个操作数不同,结果是1。怎样将第n位取反呢?如果第n位比特为1,将它与1进行异或运算结果就是0,如果它是0,那么它与1异或运算的结果就是1。于是这一位就取反了。
小例子,假设要将值01110101的第5位取反:
1 01110101 2^ 00100000 3 -------- 4 01010101
同样的数,第5位是0结果如下:
1 01010101 2^ 00100000 3 -------- 4 01110101
注意到没?对一个数进行两次同样的异或运算后结果还是原来的数,这个漂亮的特性可以用在许多编程问题中。