有趣的二进制—高效位运算
原文地址:https://my.oschina.net/u/1859679/blog/868056
基础
位操作符
符号 | 含义 | 规则 |
---|---|---|
& | 与 | 两个位都为1时,结果为1 |
| | 或 | 有一个位为1时,结果为1 |
^ | 异或 | 0和1异或0都不变,异或1则取反 |
~ | 取反 | 0和1全部取反 |
<< | 左移 | 位全部左移若干位,高位丢弃,低位补0 |
>> | 算术右移 | 位全部右移若干位,,高位补k个最高有效位的值 |
>> | 逻辑右移 | 位全部右移若干位,高位补0 |
注意:
1、位运算只可运用于整数,对于float和double不行。
2、另外逻辑右移符号各种语言不太同,比如java是>>>。
3、位操作符的运算优先级比较低,尽量使用括号来确保运算顺序。比如1&i+1,会先执行i+1再执行&。
应用实例
很棒的应用实例,你可以mark一下,方便以后对照使用。
1、混合体
位运算实例
位运算 | 功能 | 示例 |
---|---|---|
x >> 1 | 去掉最后一位 | 101101->10110 |
x << 1 | 在最后加一个0 | 101101->1011010 |
x << 1 | 1 | 在最后加一个1 | 101101->1011011 |
x | 1 | 把最后一位变成1 | 101100->101101 |
x & -2 | 把最后一位变成0 | 101101->101100 |
x ^ 1 | 最后一位取反 | 101101->101100 |
x | (1 << (k-1)) | 把右数第k位变成1 | 101001->101101,k=3 |
x & ~ (1 << (k-1)) | 把右数第k位变成0 | 101101->101001,k=3 |
x ^(1 <<(k-1)) | 右数第k位取反 | 101001->101101,k=3 |
x & 7 | 取末三位 | 1101101->101 |
x & (1 << k-1) | 取末k位 | 1101101->1101,k=5 |
x >> (k-1) & 1 | 取右数第k位 | 1101101->1,k=4 |
x | ((1 << k)-1) | 把末k位变成1 | 101001->101111,k=4 |
x ^ (1 << k-1) | 末k位取反 | 101001->100110,k=4 |
x & (x+1) | 把右边连续的1变成0 | 100101111->100100000 |
x | (x+1) | 把右起第一个0变成1 | 100101111->100111111 |
x | (x-1) | 把右边连续的0变成1 | 11011000->11011111 |
(x ^ (x+1)) >> 1 | 取右边连续的1 | 100101111->1111 |
x & -x | 去掉右起第一个1的左边 | 100101000->1000 |
x&0x7F | 取末7位 | 100101000->101000 |
x& ~0x7F | 是否小于127 | 001111111 & ~0x7F->0 |
x & 1 | 判断奇偶 | 00000111&1->1 |
2、交换两数
1 int swap(int a, int b) 2 { 3 if (a != b) 4 { 5 a ^= b; 6 b ^= a; 7 a ^= b; 8 } 9 }
3、求绝对值
1 int abs(int a) 2 { 3 int i = a >> 31; 4 return ((a ^ i) - i); 5 }
4、二分查找32位整数前导0个数
1 int nlz(unsigned x) 2 { 3 int n; 4 5 if (x == 0) return(32); 6 n = 1; 7 if ((x >> 16) == 0) {n = n +16; x = x <<16;} 8 if ((x >> 24) == 0) {n = n + 8; x = x << 8;} 9 if ((x >> 28) == 0) {n = n + 4; x = x << 4;} 10 if ((x >> 30) == 0) {n = n + 2; x = x << 2;} 11 n = n - (x >> 31); 12 return n; 13 }
5、二进制逆序
1 int reverse_order(int n){ 2 3 n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); 4 n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); 5 n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); 6 n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); 7 n = ((n & 0xFFFF0000) >> 16) | ((n & 0x0000FFFF) << 16); 8 9 return n; 10 }
6、 二进制中1的个数
1 unsigned int BitCount_e(unsigned int value) { 2 unsigned int count = 0; 3 // 解释下下面这句话代码,这句话求得两两相加的结果,例如 11 01 00 10 4 // 11 01 00 10 = 01 01 00 00 + 10 00 00 10,即由奇数位和偶数位相加而成 5 // 记 value = 11 01 00 10,high_v = 01 01 00 00, low_v = 10 00 00 10 6 // 则 value = high_v + low_v,high_v 右移一位得 high_v_1, 7 // 即 high_v_1 = high_v >> 1 = high_v / 2 8 // 此时 value 可以表示为 value = high_v_1 + high_v_1 + low_v, 9 // 可见 我们需要 high_v + low_v 的和即等于 value - high_v_1 10 // 写简单点就是 value = value & 0x55555555 + (value >> 1) & 0x55555555; 11 value = value - ((value >> 1) & 0x55555555); 12 13 // 之后的就好理解了 14 value = (value & 0x33333333) + ((value >> 2) & 0x33333333); 15 value = (value & 0x0f0f0f0f) + ((value >> 4) & 0x0f0f0f0f); 16 value = (value & 0x00ff00ff) + ((value >> 4) & 0x00ff00ff); 17 value = (value & 0x0000ffff) + ((value >> 8) & 0x0000ffff); 18 return value; 19 20 // 另一种写法,原理一样,原因在最后一种解法中有提到 21 //value = (value & 0x55555555) + (value >> 1) & 0x55555555; 22 //value = (value & 0x33333333) + ((value >> 2) & 0x33333333); 23 //value = (value & 0x0f0f0f0f) + ((value >> 4) & 0x0f0f0f0f); 24 //value = value + (value >> 8); 25 //value = value + (value >> 16); 26 //return (value & 0x0000003f); 27 }
-----------------------end-------------------------