Lecture 3 Bit Hacks
课程内容
备注:以下并未列出课程全部内容,只是将我理解并觉得用处比较多的地方列了出来。
Binary Representation
虽然大家早已经知道了二进制原码、反码、补码的意思,不过这里提供了另一种解释方法。很有意思。这里记录下来。
设x={xw−1xw2...x0}{x=\{x_{w-1}x_{w_2}...x_0\}}x={xw−1xw2...x0}是一个w位的无符号整数,二进制表示为:
x=Σk=0w−1xk2k
x=\Sigma^{w-1}_{k=0}x_k2^k
x=Σk=0w−1xk2k
有符号整数(补码)的表示是:
x=(Σk=0w−2xk2k)−xw−12w−1
x=(\Sigma^{w-2}_{k=0}x_k2^k)-x_{w-1}2^{w-1}
x=(Σk=0w−2xk2k)−xw−12w−1
其中,xw−1{x_{w-1}}xw−1是符号位。
Set the kth Bit
- 【问题】将x的第k位设为1.
- 【方法】左移、或。
y = x | (1 << k)
- 【备注】
1 << k
就是第k位的mask
(掩码)。
Clear the kth Bit
- 【问题】将x的第k位设为清0.
- 【方法】左移、取反、与。
y = x & ~(1 << k)
Toggle the kth Bit
- 【问题】将x的第k位取反。
- 【方法】左移、或。
y = x ^ (1 << k)
Extract a Bit Field
- 【问题】从x中提取第shift位
- 【方法】掩码、右移。
(x & mask) >> shift
Set a Bit Field
- 【问题】将x的shift位设为y。
- 【方法】掩码取反、或上右移之后的值。
x = (x & ~mask) | (y << shift)
No-Temp Swap
- 【问题】不用临时变量,将x、y交换。
一般有:
t = x;
x = y;
y = t;
使用二进制方式:
x = x ^ y;
y = x ^ y;
x = x ^ y;
- 【原理】xor的特性:
(x ^ y) ^ y = x
- 【性能】虽然没有创建临时变量,但是在现代CPU上,不能很好的利用指令级并行(Instruction-Level Parallelism, ILP)。
No-Branch Minimum
- 【问题】找到x、y中的最小值。
r = y ^ ((x ^ y) & -(x < y))
- 【备注】编译器可能会做得更好。
Modular Addition
- 【问题】计算
(x + y) mod n
。
// 基础版
r = (x + y) % n;
// 没有除法,无法预测的分支。
z = x + y;
r = (z < n) ? z : z - n;
// use bit hack
z = x + y;
r = z - (n & -(z >= n));
Least-Significant 1
- 【问题】计算x中最低有效位中的1的掩码。
r = x & (-x)
Population Count
- 【问题】计算x中的1的个数。
// 基础版
for (r = 0; x != 0; ++r)
x &= x - 1;
- 【注意】最坏情况下,要循环x的位数次。
- 查表的方法
static const int count[256] =
{0, 1, 1, 2, 1, 2, 2, 3, 1, ..., 8};
for (int r = 0; x != 0; x >>= 8)
r += count[x & 0xFF];
表中的数据是256个,记录了每个数(数组的index)对应的1的个数。
- 【相关指令】现代CPU上,统计1的数量的函数,都已经有了相应的指令,比任何人写出的代码都要更快。如在GCC中,有函数:
int __builtin_popcount(unsigned int x);
TODO
英语学习
- ^:读作“caret sign”。
- ~:读作“tilde”。
- caveat:警戒、告诫。