在研读Redis代码的时候偶然发现了这个网页:http://graphics.stanford.edu/~seander/bithacks.html,觉得很有趣于是就着位算法这个主题以Bit Twiddling Hacks的内容为主进行了一番梳理。后面会陆续补充更多的算法和应用场景与案例。
文章修改记录
日期 | 内容 |
2020-02-23 | 版本v1.0,基础内容整理,少量实验测试和证明 |
基础内容
运算符 |
特性 |
与 &,或 | |
相同运算符满足交换律、结合律,不同运算符满足分配律 |
非 ~,负 - |
~即按位取反; 按位取反再加1即得负数(补码表示),~v + 1 = -v; |
异或 ^ |
相同bit得0,不同bit得1; 任意数和0异或不变,任意数和1异或则取反,任意数和自己异或得0; 异或可展开:a^b = (~a & b) | (a & ~b); |
计算整数的符号
正整数返回+1,零返回0,负整数返回-1
四种算法,v=1234,循环一亿次记下总时间,单位毫秒:
算法 |
第一次 |
第二次 |
第三次 |
平均 |
|
1 |
v > 0 ? 1 : (v < 0 ? -1 : 0) |
133 |
134 |
133 |
133.3 |
2 |
(v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1)) |
101 |
102 |
102 |
101.7 |
3 |
(v > 0) - (v < 0) |
101 |
100 |
100 |
100.3 |
4 |
(v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)) |
99 |
100 |
99 |
99.3 |
观察发现v取负数或0的时候,算法1的执行时间和算法2、3、4差不多。
测试环境是Intel i9-9900K,Windows10 Pro版,MinGW-W64,gcc 8.1.0-posix-sjlj-rt_v6-rev0,可见其分支指令的性能还是不错的。
检测两个整数有不同的符号
int x, y; // 输入值
bool f = ((x ^ y) < 0); // 当且仅当x和y的符号不同的时候,返回true,相比用乘法判断,对于较大的x和y不会溢出
计算两个整数的最小和最大值
同样是一亿次计算统计总时间:
算法 |
第一次 |
第二次 |
第三次 |
平均 |
|
1 |
x < y ? x : y // min(x, y) x > y ? x : y // max(x, y) |
148 |
148 |
148 |
148 |
2 |
y ^ ((x ^ y) & -(x < y)) // min(x, y) x ^ ((x ^ y) & -(x < y)) // max(x, y) |
134 |
135 |
136 |
135 |
测试环境是Intel i9-9900K,Windows10 Pro版,MinGW-W64,gcc 8.1.0-posix-sjlj-rt_v6-rev0
判断一个整数是否是2的幂
unsigned int v; // 待判断的值
bool f = (v & (v - 1)) == 0; // 这里不包括0
bool f = v && !(v & (v - 1)); // 这里包括0
按条件设置或清除bit位(不带分支判断)
bool f; // 条件标记
unsigned int m; // bit掩码
unsigned int w; // 被修改的word,逻辑: if (f) w |= m; else w &= ~m;
w ^= (-f ^ w) & m;
// 或者对于超标量CPU:
w = (w & ~m) | (-f & m);
证明:
1. w ^= (-f ^ w) & m;
当f=1(true)时,表达式可写为w ^ ((-1 ^ w) & m),因为-1为全1,w无符号,所以-1 ^ w = ~w,表达式化简为w ^ (~w & m),根据异或的性质a ^ b = (~a & b) | (a & ~b),展开表达式为(~w & ~w & m) | (w & (~(~w & m))) = (~w & m) | (w & (w | ~m)) = (~w & m) | (w | (w & ~m)) = (~w & m) | w = (~w | w) & (m | w) = m | w
当f=0(false)时,表达式可写成w ^ ((0 ^ w) & m) = w ^ (w & m) = (~w & w & m) | (w & ~(w & m)) = 0 | (w & (~w | ~m)) = (w & ~w) | (w & ~m) = w & ~m
2. w = (w & ~m) | (-f & m);
当f=1(true)时,表达式可写为(w & ~m) | (-1 & m) = (w & ~m) | m = (m | w) & (m | ~m) = m | w
当f=0(false)时,表达式可写成(w & ~m) | (0 & m) = w & ~m
证毕