本篇的8个较复杂的位操作
下面介绍本篇的重点:8个较复杂的位操作。要理解这些位操作并不容易,我将使用文字+图解的方式来描述,力求讲明白,需要说明的是,为了作图方便,我假设数据都是char类型(只有8个bit),这些位操作对其他类型的数据同样适用。
1. 将最右侧的1翻转成0:x&(x-1)
假设x=01110100,那么x,x-1和x&(x-1)的二进制表示分别如下:
| x: |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
| x-1: |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
1 |
| X&(x-1): |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
可以看出,x-1的功能是:将最右侧的1翻转了,并让其后的0变成了1,而保持了其他位不变。然后x&(x-1)就使得x的最右侧的1翻转成0。
该位运算在上一篇中被用来计算二进制中有多少个1,其思路是:不停的翻转右侧的1,知道将该数翻转成0为止。
2. 向右连续传播最右侧的1位:x|(x-1)
比如说x=00101000,那么x,x-1和x|(x-1)的二进制表示分别如下:
| x: |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
| x-1: |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
| X|(x-1): |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
从上面的过程可以看出x|(x-1)使得x中最右边的1的右边的0都被1填充了,注意这不能理解成:翻转最右侧的连续的0,而应该理解成用最右侧的1填充了它右边的所有0位。或许你要问为什么,那么你假设x =00101001,那么x|(x-1)仍然等于00101001,它并没有将最右侧的两个0翻转!
3. 检查无符号数x是否是2的整数次幂:if((x&(x-1))==0)return true;
由1可知:x&(x-1)的作用是将最右侧的1翻转为0,假如翻转之后结果变成了0,则说明原来的数x的二进制表示中只包含一个1,那么它必定是2的整数次幂。这个简单,我就不图示了。记住:2的整数次幂跟2的整数次幂减1进行与运算的结果必然为0。
4. 检查无符号数x是否等于2的整数次幂减1:if((x&(x+1))==0) return true;
2的整数次幂减1再加1之后就等于2的整数次幂,于是可以用3的方法来判断,这也非常简单,不再图示。
5. 将右侧的连续1位串翻转成0位串,其他保持不变:((x|(x-1))+1)&x
比如x等于00101100,那么
| x: |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
0 |
| x-1: |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
| X|(x-1): |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
| (X|(x-1))+1 |
0 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
| ((X|(x-1))+1)&x |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
由2得知:x|(x-1) 使得x中最右边的1的右边的0都被1填充了;而(x|(x-1))+1 就使得x中那个连续的1位串和它右边的0位串都变成0,并且将连续的1位串的左边那位0变成了1,并保持其他位不变;(x|(x-1)+1)&x使得x中那个连续的1位串和它右边的所有位都置为0,而保持那个连续的1位串的左边保持不变,于是就实现了将最右边的连续的1位串翻转的功能。
文字解释绕来绕去的,非常拗口啊,没办法啊,这个关系本省就绕,希望不会把各位绕糊涂了。
6. 检查无符号整数x是否等于2的两个整数次幂之差: if((((x|(x-1))+1)&x)==0)==0) return true;
所谓2的两个整数次幂之差就是2k-2j,比如k等于6,j等于2,那么26的二进制是:00100000,22的二进制表示为00000100,26-22的二进制表示为00111100,由此可以看出2k-2j的特征是:二进制表示形式中从第j位到第k-1位都是1(从0开始数),其他bit位都是0。
由上可知:通过位运算在保持其他bit位不被改变的情况下,将这些为1的bit位全部翻转为0,如果所得的结果等于0,那么原来的数就是一定是2的两个整数次幂之差!事实上5中已经介绍了((x|(x-1))+1)&x的功能就是将x的二进制表示中右侧连续的1位串翻转为0位串,因此只需要应用3计算((X|(x-1))+1)&x的值,然后判断它是否等于0即可,为了说明这个问题,我再图示演示了这一过程,这里的x等于00111100
| x: |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
| x-1: |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
| X|(x-1): |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
| (X|(x-1))+1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
| ((X|(x-1))+1)&x |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
从上表看出,由于最后计算结果等于0,所以x就是两个2的整数次幂的差,其实x = 60 = 26-22。