一些位运算的技巧整理
扩展阅读:
- 一位园友的总结: https://www.cnblogs.com/xiaohutu/p/10951911.html
- 斯坦福的位运算技巧大全:http://graphics.stanford.edu/~seander/bithacks.html
- 推荐神书《Hackers Delight》
1.判断符号是否相同(Math.Sign)
a^b >= 0
2.求两个整数的平均值
(x + y) >> 1;
3.判断基偶(比%2要快)
a & 1
结果0为偶数,为1是基数
4.判断一个数是不是2的幂
((x&(x-1))==0)&&(x!=0)
5.对一个32位数快速sign,正数为1负数为-1
x>>31 | 1
6.借助德布莱英序列(DeBruijn),从右取当前Mask第一个1的位数(注意传入值必须是uint,否则出错):
int[] multiplyDeBruijnBitPosition = new int[32] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; uint x = 1 << 7; uint magicNumber = 0x077CB531; int bitPosition = multiplyDeBruijnBitPosition[((x & (uint) -x) * magicNumber) >> 27]; Debug.Log("bitPosition: " + bitPosition);//7
7.左位移和右位移的非2次方数字乘除法规则
(出处:https://www.cnblogs.com/xiaoyaodijun/p/6744057.html)
向左位移,相当于当前参数乘以2的位移次方 3<<1 等于 3*(2的一次方) 等于 3*2 3<<2 等于 3*(2的2次方) 等于 3*4 3<<3 等于 3*(2的3次方) 等于 3*8 3<<4 等于 3*(2的4次方) 等于 3*16
向右位移,相当于当前参数除以2的位移次方 16>>1 等于 16/(2的一次方) 等于 16/2 16>>2 等于 16/(2的2次方) 等于 16/4 16>>3 等于 16/(2的3次方) 等于 16/8 16>>4 等于 16/(2的4次方) 等于 16/16
8.循环位移
即: 0000 0001 0000 >> 0000 1000 0000
需要借助另一个变量才可实现,如byte要转到int里再做位移。
实现思路是在高位上做一个镜像来标记,位移完做mask,这时外面移进来的镜像值刚好被mask到,从而实现循环。
代码:
private byte BitLoopShift(byte x, bool leftOrRight) { uint y = x; y <<= 8; if (leftOrRight) { y |= x; y <<= 1; } else { uint x2 = (uint)x << 16; y |= x2; y >>= 1; } uint mask = 0b_0000_0000____0000_0000____1111_1111____0000_0000; y &= mask; y >>= 8; return (byte)y; }
Unity下测试:
private void Start() { for (byte i = 0, j = 1; i < 16; i++) { byte shiftBefore = j; byte shiftAfter = BitLoopShift(shiftBefore, true); j = shiftAfter; Debug.Log("[<<] i: " + i + " shiftBefore:" + Convert.ToString(shiftBefore, 2) + " shiftAfter: " + Convert.ToString(shiftAfter, 2)); } for (byte i = 0, j = 0b_1000; i < 16; i++) { byte shiftBefore = j; byte shiftAfter = BitLoopShift(shiftBefore, false); j = shiftAfter; Debug.Log("[>>] i: " + i + " shiftBefore:" + Convert.ToString(shiftBefore, 2) + " shiftAfter: " + Convert.ToString(shiftAfter, 2)); } }
9.去掉二进制数最右边的1
11 > 10
1010 > 1000
0 > 0
x & (x - 1)
如果一个可存叠加状态的state变量,2个8位分别存2个叠加状态,清理低位数据时刚好可以用这个功能。
10.快速隔离二进制数最右边的1
1010 > 10
x & (-x)
这个配合希尔伯特曲线或者z曲线,可以做高效的范围查询。
11.快速判断二进制位是否每一位都是1
if ((i & (i + 1)) == 0)//11111 { //每一位都是1 }
12.从最右侧的一个1向右侧填充所有1
101000 > 101111
1010 > 1011
1111 > 1111
i | (i - 1)
13.异或可以实现二进制加法
100 xor 010 = 110
4 + 2 = 6