# Bit Twiddling Hacks 解析 (二)
下一篇:[Bits Twiddling Hacks 解析 (三)](https://hackmd.io/@0xff07/WRYYYYYYYYYY)
上一篇:[Bits Twiddling Hacks 解析 (一)](https://hackmd.io/@d4cWS3kPSNiNdRbaqbKmqQ/ORAORAORAORA)
[TOC]
## 不用位元計算的技巧
這種技巧主要是建立在算術之上,類似的比如國小計算除以 9 的餘數,只要把每個位數加起來再除以 9 就好,不用真的做一次除法。
下面這邊有一些技巧是建立在類似的觀念上,僅利用加減乘除,沒有用位元運算。在十進位中大部分都有對應的做法,所以可以借助已經熟悉的十進位進行理解。
這種做法可以讓運算子看起來很少,而且有種到處都是 magic number 的感覺,比如 [3 個運算計算 14 位元整數的 population count](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64) 其中一個解法就有用到。
### 用乘法重複一組數字
先看十進位的例子:
$$
1,001,001,001,001,001 \times 987 = 987,987,987,987,987,987
$$
主要就是某個數字,乘上重複的 $100...001$ ,就會把本來的數字重複很多次。不管是寫成科學記號或是用直式乘法來證明都很容易看出來這件事。
接下來看看一個 16 進位的版本:
$$
\mathtt{res = 0x200040008001ULL} \times \mathtt{0xBAD}
$$
這個結果寫成 16 進位的話,會是 `0x175a2eb45d68bad` ,乍看之下不是很好懂。但如果用直式乘法去思考的話,其實會是:
```c=
res = (0xBAD) +
(0xBAD << 15) +
(0xBAD << 30) +
(0xBAD << 45);
```
原理也是一樣。如果把上面的東西寫成二進位的話,其實是在做:
```c=
mask = 0b1000000000000001000000000000001000000000000001;
bad = 0b101110101101; // 0xBAD
res = mask * bad;
```
因為 64 位元的關係,把完整的數字寫出來實在是很長,所以大部分都會寫 16 進位,然後就會變成一個有點令人費解的樣子。
### 用乘法壓縮位元
這個用直式乘法非常好證明。舉例來說,現在有一個 64 位元無號數的 ==2 進位== 形式長這樣:
```c
tmp = 0000000H 000000G0 00000F00 0000E000 000D0000 00C00000 0B000000 A0000000
```
其中 A、B、C、D、E、F、G、H 都是 `0` 或 `1`。如果做:
```c
tmp = (tmp << 0) +
(tmp << 8) +
(tmp << 16) +
(tmp << 24) +
(tmp << 32) +
(tmp << 40) +
(tmp << 48) +
(tmp << 56);
```
那可以發現計算結果最高位的 8 個位元,會是 (觀察對角線方向):
```c
0000000H
000000G0
00000F00
0000E000
000D0000
00C00000
0B000000
A0000000
---------(+)
ABCDEFGH
```
回到剛剛的計算:那一連串的位移其實可以簡化成:
```c
tmp = tmp * 0x0101010101010101ULL;
```
所以總結以上,做:
```c
res = (tmp * 0x0101010101010101ULL) >> 56;
```
就會有把稀疏的位元壓縮再一起的效果。
### 用取餘數計算位數和
有一些問題的思路可以從「計算位數和」下手。比如說 population count 就是在算每個位數的和; 而 parity bit 就是算完位數和之後,再看奇偶性 (比如做 `x & 1`)。
==暖身:以 10 為基數的位數和==
這件事情的原理,跟國小時判斷一個數是不是 9 的倍數,使用「(位數和) % 9,看看餘數是不是 0」,的原理是很像的。比如說想要計算:
$$
9487 \div 9
$$
的餘數,那麼小時候大家都學過,去計算:
$$
(9 + 4 + 8 + 7) \div 9
$$
的餘數就好。這是因為對於任意非負整數 $k$,有:
$$
10^k = \underbrace{999...9}_{k} + 1
$$
因此,假定一個數的 10 進位表示法是 $a_k \dots a_2a_1a_0$,或是說:
$$
n = \sum_{i = 0}^{k}a_i\times 10^i
$$
那麼可以把所有的 $10^i$ 拆成 $(\underbrace{999...9}_{i} + 1)$,然後經過一點計算之後:
$$
\begin{align}
n &= \sum_{i = 0}^{k}a_i\times (\underbrace{999\dots 9 + 1}_{i}) & \mod 9\newline
&= \sum_{i = 0}^{k}a_i\times (\underbrace{999\dots 9}_{i}) + (a_i \times 1) & \mod 9 \newline
&= \sum_{i = 0}^{k} a_i & \mod 9
\end{align}$$
但如果==位數的總和比 9 小==,那麼有 `mod` 跟沒有 `mod` 結果是一樣的,那麼除法計算的商根本就是 0。也就是說 $\text{mod}$ 根本可以拿掉,像這樣:
$$
\begin{align}
n &= \sum_{i = 0}^{k}a_i \mod 9\newline
&= \sum_{i = 0}^{k}a_i \quad \left(\text{if }\sum_{i = 0}^{k}a_i < 9\right)
\end{align}
$$
比如說,想計算 $112211$ 的位數和是 $1 + 1 + 2 + 2 + 1 + 1 = 8$,剛好就會是它除以 $9$ 的餘數。
==例子:16 進位的位數和==
現在我們把基數從 $10$ 換成 $16$。比如說,想要計算 `n = 0x11101111` 的位數和。這個數字其實是:
$$
n = \sum_{i = 0}^{7} b_i \times 16^i
$$
其中,裡面任意 $b_i$ 只會是 $0$ 或 $1$。仿照上面的拆法。不過因為現在 base 是 16,所以就把 $16^{i}$ 拆成$\mathtt{0x\underbrace{FF...FF}_{i}} + 1$
$$
\begin{align}
n &= \sum_{i = 0}^{7} b_i \times 16^i &\mod 15\newline
&= \sum_{i = 0}^{7} b_i \times (\mathtt{0x\underbrace{FF...FF}_{i}} + 1) &\mod \mathtt{0xF}\newline
&= \sum_{i = 0}^{7} b_i &\mod \mathtt{0xF} \newline
&= \sum_{i = 0}^{7} b_i & \text{if } \sum_{i = 0}^7{b_i} < \mathtt{0xF}
\end{align}
$$
所以可以一眼知道位數和就是每個位元加起來,就是 $7$。
### 小提示
:::info
後面有很多利用乘法的招式,大致上都是遵照下面的形式:
1. 在 64 位元中,用乘法重複一個 byte 的內容很多次。
2. 依照特定間隔取出位元,使得該 byte 中每個位元恰好在 mask 後的結果出現一次。
3. 用取餘數計算所有位元的加總(比如 population count 中的 [方法四(之一):加總位數和 (14 位元數值 + 64 位元運算)](https://hackmd.io/@d4cWS3kPSNiNdRbaqbKmqQ/MUDAMUDAMUDA#方法四(之一):加總位數和-14-位元數值--64-位元運算); 或用上面的乘法把所有位元壓縮在一起(比如反轉所有位元中的[方法四:用乘法反轉一個-byte](https://hackmd.io/@d4cWS3kPSNiNdRbaqbKmqQ/MUDAMUDAMUDA#方法四:用乘法反轉一個-byte))
舉例來說下面三者:
```c=
/* 對一個 byte 做 population count */
(b * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;
/* 計算一個 byte b 的 parity bit */
(((b * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1;
/* 反轉一個 byte b */
((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
```
:::
## Population Count
這個經典的問題是這樣:給定一組 bit mask,要問裡面有多少個 1。最天真的方法就是用迴圈:
```c
/* @n : the integer to be counted */
/* @res: the pop count result */
while(res = 0; n ; n >>= 1) {
res += (n & 1);
}
```
不過這樣明顯會出現很多分支,所以接下來就要思考改良的版本。
### 方法一:清除所有位元
原文:[Counting bits set, Brian Kernighan's way](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan)
```c=
for (res = 0; n; n &= n-1) {
res++;
}
```
這個是用上面「清除最低位元」的做法去做。在位元很稀疏的時候,有機會比天真的做法快。不過還是免不了使用 branch。
### 方法二:暴力查表
原文:[Counting bits set by lookup table](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable)
~~就是那個傳說中把結果存在月球上的做法啊。~~
這個做法是建立一個大小是 256 的陣列 `table` 當作表,枚舉所有 8 位元的整數所有可能數值中,對應的 1 的個數。比如說我想要查 `73` 這個數字中,有多少個 1,那就去找 `table[73]`。所以這個有點大的表會像是:
```c
unsigned char table[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
res = table[n];
```
不過這樣打起來有點煩瑣,所以可以用巨集的技巧簡化:
```c=
unsigned char table[256] = {
# define B2(n) n, n+1, n+1, n+2
# define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2)
# define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2)
# define B8(n) B6(n), B6(n+1), B6(n+1), B6(n+2)
B8(0)
};
res = table[n];
```
這個巨集其實是用下面的遞迴結構:
$$
\mathtt{pop(n([N:0])}\begin{cases}
0 + \mathtt{pop(n[N-2:0])} & \text{if }\quad \mathtt{n[N:N-1] = 0} \newline
1 + \mathtt{pop(n[N -2:0])} & \text{if }\quad \mathtt{n[N:N-1] = 1} \newline
1 + \mathtt{pop(n[N-2:0])} & \text{if }\quad \mathtt{n[N:N-1] = 2} \newline
2 + \mathtt{pop(n[N-2:0])} & \text{if }\quad \mathtt{n[N:N-1] = 3}
\end{cases}
$$
原理就是照最高 2 個位元進行討論。他有可能是 `0b00`, `0b01`, `0b10`, `0b11`,而 pop count 的結果就是「最高 2 位元的 1 的數目」加上「其餘低位元的 1 的數目」。
對於其他 8 的倍數寬度的數值型別,只要多做幾次就可以了:
```c=
unsigned char table[256] = {
# define B2(n) n, n+1, n+1, n+2
# define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2)
# define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2)
# define B8(n) B6(n), B6(n+1), B6(n+1), B6(n+2)
B8(0)
};
res = table[n & 0xff] +
table[(n >> 8) & 0xff] +
table[(n >> 16) & 0xff] +
table[(n >> 24) & 0xff];
```
另外一個看起來很酷的變形是用一個 `unsigned char` 指標去指要計算的整數,然後就可以用中括號去存取每個 byte:
```c
unsigned char * p = (unsigned char *) &n;
res = table[p[0]] +
table[p[1]] +
table[p[2]] +
table[p[3]];
```
### 方法三 (之一):Divide and Conquer
原文:[Counting bits set, in parallel](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel)
這個應該是最有名的做法吧?主要的思路是這樣:
```c
v = (v & 0x55555555) + ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
v = (v & 0x0F0F0F0F) + ((v >> 4) & 0x0F0F0F0F);
v = (v & 0x00FF00FF) + ((v >> 8) & 0x00FF00FF);
v = (v & 0x0000FFFF) + ((v >> 16) & 0x0000FFFF);
res = v;
```
但裡面給出一個等價,但是運算次數更少的版本:
```c=
c = v - ((v >> 1) & 0x55555555);
c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
c = ((c >> 4) + c ) & 0x0F0F0F0F;
c = ((c >> 8) + c ) & 0x00FF00FF;
c = ((c >> 16) + c) & 0x0000FFFF;
```
第一行做法為什麼會對,可以窮舉所有可能的狀況:
```c
0b11 - (0b11 >> 1) = 2
0b10 - (0b10 >> 1) = 1
0b01 - (0b01 >> 1) = 1
0b00 - (0b00 >> 1) = 0
```
所以就剛好跟原先的第一步是一樣的。接下來的做法大上就是跟原先一樣。
### 方法三 (之二):Divide and Conquer + 乘法
原文:[Counting bits set, in parallel](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel) 的下面
概念大致上跟上面一樣,只是後面用乘法做:
```c=
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
r = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
```
最後面乘法的作用是像下面這樣:
```c
c = v - ((v >> 1) & 0x55555555);
c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
c = ((c >> 4) + c ) & 0x0F0F0F0F;
r = (c + (c << 8) + (c << 16) + (c << 24)) >> 24;
```
### 方法四(之一):加總位數和 (14 位元數值 + 64 位元運算)
原文:[Counting bits set in 14, 24, or 32-bit words using 64-bit instructions](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64)
這邊使用到另外一個概念:==population count = 加總所有位數==。所以一個可能的做法是下面這樣:
```c=
int res = 0;
unsigned long long tmp = (n) + (n << 15) + (n << 30) + (n << 45);
for (int i = 0; i < 14; i++) {
res += (tmp & 1);
tmp >>= 4;
}
```
大致上就是:
1. 重複這個數字很多次
2. 利用「原數字重複的週期」跟「取位元的週期」互質來做到「恰好每個位元都出現一次」
3. 把取出來的位元加總。
不過在文章中出現的程式碼很短,是這樣:
```c
res = (n * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;
```
如果可以一眼看出來在做什麼的話,那可以跳過。如果看不出來的話,下面接著解釋。這段程式只有 3 個運算,分別就代表上面的 3 個步驟:
1. `* 0x200040008001ULL`:重複原來的數字
2. `& 0x111111111111111ULL`:利用「原數字重複的週期」跟「取出位元的週期」互質來做到「恰好每個位元都出現一次」。這邊利用「原數字重複的週期」是 15; 「取出位元的週期」是 4。
3. `% 0xf`:把取出來的位元加總。
#### 1. n * 0x200040008001ULL
如果觀察一下 `0x200040008001ULL`,會發現他是不斷重複的 `0b000 0000 0000 0001`(14 個 `0`,1 個 `1`)。比如說如果數字是 `0xBAD`,那乘上 `0x200040008001ULL` 之後會變成:
```c
tmp1 = 0xBAD +
0xBAD << 15 +
0xBAD << 30 +
0xBAD << 45;
```
反正就是把 0xBAD 重複寫很多次。但關鍵就是:他會以==每 15 個位元為週期重複==。
#### 2. & 0x111111111111111ULL
如果把每個位元用 `0DCBA9876543210` 表示每個位元,(==比如第 `A` 代表第 10 個位元、`4` 代表第 4 位元等等==)的話,上面那個過程其實是「讓 `n` 中每一個位元恰好在結果中出現 1 次」。如果用 `0DCBA9876543210` 分別代表 0 ~ 15 個二進位中的位數的話,上面的步驟其實是照下面的過程取出對應的位數:
$$
\begin{align}
\mathtt{0DC\underset{\uparrow}{B}A98\underset{\uparrow}{7}654\underset{\uparrow}{3}210\ \underset{\uparrow}{0}DCB\underset{\uparrow}{A}987\underset{\uparrow}{6}543\underset{\uparrow}{2}10\ 0\underset{\uparrow}{D}CBA\underset{\uparrow}{9}876\underset{\uparrow}{5}432\underset{\uparrow}{1}0\ 0D\underset{\uparrow}{C}BA9\underset{\uparrow}{8}765\underset{\uparrow}{4}321\underset{\uparrow}{0}}\newline
\mathtt{000\underset{\uparrow}{1}000\underset{\uparrow}{1}000\underset{\uparrow}{1}000\ \underset{\uparrow}{0}000\underset{\uparrow}{1}000\underset{\uparrow}{1}000\underset{\uparrow}{1}00\ 0\underset{\uparrow}{1}000\underset{\uparrow}{1}000\underset{\uparrow}{1}000\underset{\uparrow}{1}0\ 00\underset{\uparrow}{1}000\underset{\uparrow}{1}000\underset{\uparrow}{1}000\underset{\uparrow}{1}}\newline
\hline
\mathtt{000\underset{\uparrow}{B}000\underset{\uparrow}{7}000\underset{\uparrow}{3}000\ \underset{\uparrow}{0}000\underset{\uparrow}{A}000\underset{\uparrow}{6}000\underset{\uparrow}{2}00\ 0\underset{\uparrow}{D}000\underset{\uparrow}{9}000\underset{\uparrow}{5}000\underset{\uparrow}{1}0\ 00\underset{\uparrow}{C}000\underset{\uparrow}{8}000\underset{\uparrow}{4}000\underset{\uparrow}{0}}
\end{align}
$$
也就是依照:
```
n[0], n[4], n[8], n[12],
n[1], n[5], n[9], n[13],
n[2], n[6], n[10], n[14],
n[3], n[7], n[11]
```
的順序,把第 `i` 位元取出。其中 `n[i]` 是由最低位數來第 `i` 個位元。因為想不到用比較好的表達方法,所以就借用類似 verilog 的語法來表打。
關鍵是:==4 跟 15 互質,所以這樣就保證恰好取到 `n` 裡面的所有位數==。為了方便,暫時用 `tmp2` 來稱呼到目前為止的計算結果:
$$
\mathtt{tmp2 := (n * 0x200040008001ULL\ \&\ 0x111111111111111ULL)};
$$
這個結果會是個位元形如下面這樣的數字:
$$
\mathtt{tmp2 = 0b0...\underbrace{000n_{11}000n_{7}000n_{3} \dots000n_1000n_{12}000n_8000n_4000n_0}_{4 \times 14 = 56 \text{ bits}}}
$$
現在我們有什麼?
1. 所有 $n$ 的位元都在裡面剛好出現過一次
2. 目前的計算結果是:
$$
\begin{align}
\text{tmp2} = n_0 &\cdot 16^0 \newline
n_4 &\cdot 16^1 &+ \newline
n_8 &\cdot 16^2 &+ \newline
n_{12} &\cdot 16^{3} &+ \newline
n_{1} &\cdot 16^{4} &+ \newline
n_{4} & \cdot 16^{5} &+ \newline
\dots \newline
n_{7} &\cdot 16^{12} &+ \newline
n_{11} & \cdot 16^{13}
\end{align}
$$
現在所有 `n` 的位元都恰好在 `tmp2` 中出現一次,其他位元都是 0。現在只欠東風:==把 `tmp` 的每個位元加總。==
#### 3. % 0xF
因為現在基數是 16,加上位數和最大就是 14 (14 個位元都是 1) < (16 - 1)。用前面[計算位數和](https://hackmd.io/@d4cWS3kPSNiNdRbaqbKmqQ/ORAORAORAORA#計算位數和)的結論可以知道:這時做 `% (16 - 1)` 就可以求出位數和。
### 方法四(之二):加總位數和 (24 位元數值 + 64 位元運算)
原文:[Counting bits set in 14, 24, or 32-bit words using 64-bit instructions](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64)
```c
res = ((n & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
res += (((n & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL)% 0x1f;
```
原理跟上面一樣,但他把「12 位元數字的,相同的構造方法,重複做 2 次。」加起來就會得到得到 24 位元的 population count。
處理 12 位元的 population count 時,重複寫原來數字對週期,從 15 位元,變成 12 位元; 以及 mask 的週期,由 4 位元變成 5 位元。其餘概念相同。
### 方法四(之三):加總位數和 (32 位元數值 + 64 位元運算)
原文:[Counting bits set in 14, 24, or 32-bit words using 64-bit instructions](https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64)
```c
res = ((n & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
res += (((n & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
res += ((n >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
```
原理跟上面一樣,變成做 12 位元的狀況做 3 次,一次是計算 32 位元中,低 12 位的位數和; 一次是計算次高 12 位元的位數和; 最後計算最高 8 位的位數和。不過反正 12 位元可以用,把 8 位元數字當成 12 位元數字照樣可以用。所以看起來就是 12 位元的做法做 3 次。
## Parity Bit
parity bit 的做法很多會跟 population count 很像,因為能做 population count 就可以用計算的結果直接去找出 parity bit。
### 方法一:直覺的方法
原文:[Computing parity the naive way](https://graphics.stanford.edu/~seander/bithacks.html#ParityNaive)
最直覺的方法就是做:
```c=
while (x) {
parity = !parity;
x = x & (x - 1);
}
```
不過照樣子會希望避開 branch 跟 jump。做法大概就是兩類:
1. 從 popullation count 的做法改來
2. parity bit 就是想辦法計算所有位元 `XOR` 起來的值。所以往這個方向做也可以。
### 方法二:暴力查表
原文:[Compute parity by lookup table](https://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable)
這個跟 population count 那邊的查表法類似。可以建立一個大小 256 的表,枚舉 `0` 到 `255` 中所有值的 parity:
```c
char b;
static const bool P[] = {
0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1
};
bool parity = P[b];
```
而這個表也有類似前面 population count 的巨集建立方式。所以程式就可以簡化成:
```c=
static const bool ParityTable256[256] =
{
# define P2(n) n, n^1, n^1, n
# define P4(n) P2(n), P2(n^1), P2(n^1), P2(n)
# define P6(n) P4(n), P4(n^1), P4(n^1), P4(n)
P6(0), P6(1), P6(1), P6(0)
};
unsigned char b; // byte value to compute the parity of
bool parity = ParityTable256[b];
```
會做一個 byte 的 parity bit,那就可以用一樣的方法去做一個 byte 的整數倍位元的 parity bit:
```c=
// Variation:
unsigned char * p = (unsigned char *) &v;
parity = ParityTable256[p[0] ^ p[1] ^ p[2] ^ p[3]];
```
或是先把狀態「壓縮」到 8 bit 裡面,再查表:
```c=
unsigned int v;
v ^= v >> 16;
v ^= v >> 8;
bool parity = ParityTable256[v & 0xff];
```
### 方法三:加總位數和 (8-bit 限定)
原文:[Compute parity of a byte using 64-bit multiply and modulus division](https://graphics.stanford.edu/~seander/bithacks.html#ParityWith64Bits)
```c=
unsigned char b; // byte value to compute the parity of
bool parity =
(((b * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1;
```
這個就是從 population count 的[方法四(之一):加總位數和 (14 位元數值 + 64 位元運算)](https://hackmd.io/@d4cWS3kPSNiNdRbaqbKmqQ/MUDAMUDAMUDA#方法四(之一):加總位數和-14-位元數值--64-位元運算)改來的。先做 population count,再看結果的奇偶性。
### 方法四:直式乘法
原文:[Compute parity of word with a multiply](https://graphics.stanford.edu/~seander/bithacks.html#ParityMultiply)
```c=
unsigned int v; // 32-bit word
v ^= v >> 1;
v ^= v >> 2;
v = (v & 0x11111111U) * 0x11111111U;
return (v >> 28) & 1;
```
做完 `v^= v>> 1` 跟 `v ^= v >> 2` 之後,每 0, 4, 8, 12 ... 24, 28 位元,都會剛好是 0 ~ 3, 4 ~ 7, 8 ~ 12 ... 24 ~ 27, 28 ~ 31 位元的 parity bit。
接著把這些位元取出來,用類似「直式乘法」的方式去思考:乘上 `0x11111111U` 時,第 28 位元其實是在做:
```c=
v = v +
(v << 4) +
(v << 8) +
(v << 12) +
(v << 16) +
(v << 20) +
(v << 24) +
(v << 28)
```
所以第 28 位元,就會是加總結果的個位數。因此把它取出來,就是 parity bit。
### 方法五:全部 XOR 起來
原文:[Compute parity in parallel](https://graphics.stanford.edu/~seander/bithacks.html#ParityParallel)
可以直接做 32 次。不過因為 XOR 有結合律跟交換律,所以可以用分治法,一直「對折」來找出 parity bit:
```c
unsigned int v; // word value to compute the parity of
v ^= v >> 16;
v ^= v >> 8;
v ^= v >> 4;
v ^= v >> 2;
v ^= v >> 1;
parity = v;
```
但是可以用一些 magic number 去減少次數:
```c=
unsigned int v; // word value to compute the parity of
v ^= v >> 16;
v ^= v >> 8;
v ^= v >> 4;
v &= 0xf;
return (0x6996 >> v) & 1;
```
大做法大致上跟上面差不多,不過只對折到寬度為 4 就不繼續對折了,而是用一個奇怪的數字 `0x9669` 去位移。這個數字其實是 `0110 1001 1001 0110`,就是上面的 `ParityTable256` 前 16 個數字壓縮進 16 位元裡面。這樣一來,只要位移並取出第 1 個位元,就可以做到跟前面查表一樣的效果。
## 反轉所有位元
原文:[Reverse bits the obvious way](https://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious)
問題:給定一個 `unsigned int v`,反轉 `v` 的所有位元。即結果 `c` 的第 `i` 位元,必須等於 `v` 的第 `31 - i` 位元。其中 `i` 的範圍是 `0 ~ 31`。
最直覺的做法就是用迴圈下去:
```c=
unsigned int v; // input bits to be reversed
unsigned int r = v; // r will be reversed bits of v; first get LSB of v
int s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end
for (v >>= 1; v; v >>= 1)
{
r <<= 1;
r |= v & 1;
s--;
}
r <<= s; // shift when v's highest bits are zero
```
### 方法一:暴力查表
跟前面兩個做法類似。先討論 8 位元的狀況,然後枚舉 0 ~ 255 中,每一個數值反轉之後的結果,存在一個大小 256 的陣列裡面:
```c=
unsigned char b;
static const unsigned char BitReverseTable256[256] = {
0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255,
};
res = table[b];
```
類似地,這個表也可以用巨集去建立:
```c=
tatic const unsigned char BitReverseTable256[256] =
{
# define R2(n) n, n + 2*64, n + 1*64, n + 3*64
# define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16)
# define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 )
R6(0), R6(2), R6(1), R6(3)
};
unsigned int v; // reverse 32-bit value, 8 bits at time
unsigned int c; // c will get v reversed
```
這個巨集運作的原理可以用這種方法思考:假定本來的 byte 的二進位形式是:
$$
\mathtt{hgfedcba}
$$
那麼這個 byte 反轉的結果,可以用下面的方法計算出來:
$$
\begin{align}
\mathtt{abcdefgh} =& \mathtt{(ab * 64)\ + } \newline
& \mathtt{(cd * 16)\ + } \newline
& \mathtt{(ef * 4 )\ + } \newline
& \mathtt{(gh * 1 )}
\end{align}
$$
這個過程可以想像成成要開下面這樣的陣列:
$$
\mathtt {int\ table[4][4][4][4];}
$$
其中:
$$
\begin{align}
\mathtt{table[hg][fe][dc][ba]} =& \mathtt{(ab * 64)\ + } \newline
& \mathtt{(cd * 16)\ + } \newline
& \mathtt{(ef * 4 )\ + } \newline
& \mathtt{(gh * 1 )}
\end{align}
$$
另外一方面,手動展開上面的巨集,就會發現:開一個 `table[4][4][4][4]` 去存取 `table[hg][fe][dc][ba]`,在 C 語言的 array subscription 下,這件事跟開一個大小 256 的表 `table[256]` ,然後直接存取 `table[hgfedcba]` 效果,是一樣的。所以就可以用上面的巨集去做。
會做 8 位元的表之後,就可以做 32 位元的反轉:
```c=
c = (BitReverseTable256[v & 0xff] << 24) |
(BitReverseTable256[(v >> 8) & 0xff] << 16) |
(BitReverseTable256[(v >> 16) & 0xff] << 8) |
(BitReverseTable256[(v >> 24) & 0xff]);
```
或是用指標:
```c=
unsigned char * p = (unsigned char *) &v;
unsigned char * q = (unsigned char *) &c;
q[3] = BitReverseTable256[p[0]];
q[2] = BitReverseTable256[p[1]];
q[1] = BitReverseTable256[p[2]];
q[0] = BitReverseTable256[p[3]];
```
### 方法二:分治法
位元逆轉其實可以用分治法去做:假設現在要反轉的是 `v[2n-1:0]`,而且現在已經有左半部的反轉結果 `left = rev(v[2n-1:n)]` 跟右半部的反轉結果 `right = rev(v[n-1:0])`,那只要把這兩半部對調,即 `{righ, left}` 就是反轉的結果。
舉例來說:假設現在想要反轉的東西是這樣:
```=
* * * * * * *
* * * * * *
* * * * *
* * * *
* * *
* *
*
```
如果已經有上下兩半反轉結果,那把這上下兩半交換,就會得到全部的反轉結果:
```=
* * * * * *
* * * * * * * *
* * * * * * * * * *
* * * * * * * * --exchange each half---> * * * *
* to get reversed seq. * * * * *
* * * * * * * *
* * * * * * * * * *
* * * * * * * * * * * *
```
然後遞迴下去:就可以知道可以用下面的順序找出 32 位元的反轉結果:
1. 找長度為 2 的反轉結果 X 16 組。接著利用上面的推論會找到
2. 長度為 4 的反轉結果 8 組,接著
3. 長度為 8 的反轉結果 4 組,接著
4. 長度為 16 的反轉結果 2 組,接著
5. 長度為 32 的反轉結果 1 組
舉實例來說,如果要反轉的位元是 `hgfe dcba`,過程會像下面這個樣子:
```
[h][g][f][e][d][c][b][a] --長度為 2 的反轉結果 4 組-->
[gh] [ef] [cd] [ab] --長度為 4 的反轉結果 2 組-->
[efgh] [abcd] --長度為 8 的反轉結果 1 組-->
[abcdefgh]
```
所以,在 32 位元的狀況下,就會看起來像下面這樣:
```c=
unsigned int v; // 32-bit word to reverse bit order
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1);
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2);
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
v = ( v >> 16 ) | ( v << 16);
```
另外,也有迴圈版的:
```c=
unsigned int s = 32 // bit size; must be power of 2
unsigned int mask = ~0;
while ((s >>= 1) > 0)
{
mask ^= (mask << s);
v = ((v >> s) & mask) | ((v << s) & ~mask);
}
```
這個版本差別是他用 top-down 的方法做 (16 位元 X2、8 位元 X4、4 位元 X8 ...),並且把 `mask` 在迴圈裡面計算。
### 方法三:「64 位元乘法 + 取餘數」反轉一個 byte
跟前面的 population count 類似的技巧:
```c=
unsigned char b; // reverse this (8-bit) byte
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
```
大致可以看出來,乘上 `0x0202020202` 就是要讓原本的 8 位元數字在 64 位元中「循環很多次」; 而 `% 1023` 看起來跟前面「計算位數和」中的技巧有點像。但那個 `& 0x010884422010` 這個數字就有點不好懂。不過,如果把整個過程展開來,就可以發現他的功用:
```
HGFEDCBA (b)
00000000 00000010 00000010 00000010 00000010 00000010 (0x0202020202)
------------------------------------------------------------------------- (*)
0000000H GFEDCBAH GFEDCBAH GFEDCBAH GFEDCBAH GFEDCBA0
00000001 00001000 10000100 01000010 00100000 00010000 (0x010884422010)
------------------------------------------------------------------------- (&)
0000000H 0000C000 G0000B00 0F0000A0 00E00000 000D0000
```
接著取 `1023` 的餘數。這個可以用前面的[計算位數和](https://hackmd.io/@d4cWS3kPSNiNdRbaqbKmqQ/MUDAMUDAMUDA#計算位數和)中的結論。因為`b * 0x0202020202ULL & 0x010884422010ULL` 有下面這個形式::
```c
tmp = (00000D0000 << 0) +
(00A000E000 << 10) +
(000B000F00 << 20) +
(0000C000G0 << 30) +
(000000000H << 40)
```
所以,他可以表示成 $2^{10}$ 為基數的形式。把以 $2^{10}$ 為基數的位數和加總起來,就會發現結果剛好是反轉之後的位元:
```c
res = 00000D0000 +
00A000E000 +
000B000F00 +
0000C000G0 +
000000000H
---------------------
00ABCFEFGH
```
### 方法四:用 64 位元乘法反轉一個 byte
這個方法跟上面很像:
```c=
b = ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
```
第一眼看過去會發現: `& 0x0884422110ULL` 這步跟前面很像,只是重複 `b` 的週期不一樣,以及最後是用「乘法」而非取「餘數」。
具體的機制可以把它前幾部展開看看:
```c
HGFEDCBA (b)
00000000 10000000 00100000 00001000 00000010 (0x0080200802)
------------------------------------------------------------------------- (*)
0HGFEDCB A00HGFED CBA00HGF EDCBA00H GFEDCBA0
00001000 10000100 01000010 00100001 00010000 (0x0884422110)
------------------------------------------------------------------------- (&)
0000E000 A0000F00 0B0000G0 00C0000H 000D0000 (=:tmp)
```
如果稍微觀察一下,可以發現如果把它視為 256 進位,並且對每一個「位數」(也就是組成這個數的每個 byte 都拿出來)做加總的話,其實就是位元組逆轉後的結果:
```
000D0000
00C0000H
0B0000G0
A0000F00
0000E000
---------(+)
ABCDEFGH
```
這件事最前面也提到:壓縮位元可以用乘法來做。把這個過程展開如下:觀察:
```clike
res = ((tmp << 0) +
(tmp << 8) +
(tmp << 16) +
(tmp << 24) +
(tmp << 32)) >> 32
```
也就是:
```clike
tmp *= 0x010101010101ULL
```
這個過程用直式展開:
```
0000E000 A0000F00 0B0000G0 00C0000H 000D0000
0000E000 A0000F00 0B0000G0 00C0000H 000D0000
0000E000 A0000F00 0B0000G0 00C0000H 000D0000
0000E000 A0000F00 0B0000G0 00C0000H 000D0000
0000E000 A0000F00 0B0000G0 00C0000H 000D0000
```
注意最中間的一行,出現了我們需要的「加總」:
```
000D0000
00C0000H
0B0000G0
A0000F00
0000E000
```
而且可以觀察到:整個過程都不會產生進位。所以最後只要把這個結果取出來就好。而可以觀察到:要取出來就是做 `>> 32`。
本來看起來應該還要用 `& 0xFF` 去取出最低的 8 位元,但因為「無號數超出範圍會自動取模數」的行為,所以會自動留下最後 8 位元的結果。
### 方法五:不用 64 位元乘法反轉 1 個 byte
```c=
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
```
跟使用 64 位元的版本比較的話:
```c
b = ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
```
會發現這個做法跟上面大致雷同,連 magic number 也一樣,只是拆成兩半做而已。原本使用 64 位元乘法的過程是這樣:
```c
HGFEDCBA (b)
00000000 10000000 00100000 00001000 00000010 (0x0080200802)
------------------------------------------------------------------------- (*)
0HGFEDCB A00HGFED CBA00HGF EDCBA00H GFEDCBA0
00001000 10000100 01000010 00100001 00010000 (0x0884422110)
------------------------------------------------------------------------- (&)
0000E000 A0000F00 0B0000G0 00C0000H 000D0000 (=:tmp)
```
如果把上面不用 64 位元乘法程式稍微改寫成這樣:
```c
l = b * 0x0802LU & 0x22110LU;
r = b * 0x8020LU & 0x88440LU;
b = (l | r) * 0x10101U >> 16;
```
那求出 `l` 與 `r` 的過程,其實就是 使用 64 位元乘法的版本切成一半而已。像下面這樣:
```c
HGFEDCBA (b)
00000000 10000000 00100000 00001000 00000010 (0x8020 0802)
-------------------------- ----------------------------------------------- (*)
0HGFEDCB A00HGFED CBA00HGF EDCBA00H GFEDCBA0
00001000 10000100 01000010 00100001 00010000 (0x08844 22110)
-------------------------- ----------------------------------------------- (&)
0000E000 A0000F00 0B0000G0(l) 00C0000H 000D0000 (r)
```
而 `(l | r)` 就會變成:
```c
0000E000 A0000F00 0B0000G0 (l)
00C0000H 000D0000 (r)
----------------------------------(OR)
0000E000 A0C00F0H 0B0D00G0 (l|r)
```
接下來做直式乘法的時候,就會變成:
```
0000E000 A0C00F0H 0B0D00G0
0000E000 A0C00F0H 0B0D00G0
0000E000 A0C00F0H 0B0D00G0
----------------------------------------------(+)
ABCDEFGH
```
## 取餘數
### 正數除以 (1 << s) 的餘數
實際上只有一行:
```c=
m = n & ((1U << s) - 1);
```
這個做法很好懂:取餘數就是取最低的 `s` 位元,而 `(1U << s) - 1` 剛好就會生出取最低位元的那個 `mask`。跟計算機組織算 direct map 是同一個算法。
不過為了避免 integer promotion 各種麻煩還是附上完整的:
```c=
const unsigned int n; // numerator
const unsigned int s;
const unsigned int d = 1U << s;
unsigned int m; // m will be n % d
m = n & ((1U << s) - 1);
```
### 正數除以 (1 << s) - 1 的餘數 (迴圈)
這個技巧就跟「十進位算除以 9 的餘數」「十六進位算除以 15 的餘數」的做法是一樣的:一直加總在那個基數下表示的各個位數,直到小於除數:
```c=
unsigned int n; // numerator
const unsigned int s; // s > 0
const unsigned int d = (1 << s) - 1;
unsigned int m; // n % d goes here.
for (m = n; n > d; n = m)
for (m = 0; n; n >>= s)
m += n & d;
// Now m is a value from 0 to d, but since with modulus division
// we want m to be 0 when it is d.
m = m == d ? 0 : m;
```
### 正數除以 (1 << s) - 1 的餘數 (查表)
既然是計算位數的總和,能不能仿照 population count 那邊的方法,用分治法來計算?答案是可以的。只是 magic number 要先建好。這邊的第一步跟 population count 的第一步是類似的,只是依照 `s` 的大小,調整「柵欄」的寬度:
```c=
unsigned int n; // numerator
const unsigned int s; // s > 0
const unsigned int d = (1 << s) - 1;
unsigned int m; // n % d goes here.
static const unsigned int M[] =
{
0x00000000, 0x55555555, 0x33333333, 0xc71c71c7,
0x0f0f0f0f, 0xc1f07c1f, 0x3f03f03f, 0xf01fc07f,
0x00ff00ff, 0x07fc01ff, 0x3ff003ff, 0xffc007ff,
0xff000fff, 0xfc001fff, 0xf0003fff, 0xc0007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff
};
m = (n & M[s]) + ((n >> s) & M[s]);
```
用 `s = 7` 舉例。為了方便,用英文字母幫這個整數的有效位數做劃分。劃分成 `EEEE DDDDDDD CCCCCCC BBBBBBB AAAAAAA`。
1. 首先,執行 `m = (n & M[s]) + ((n >> s) & M[s]`
```c
EEEE DDDDDDD CCCCCCC BBBBBBB AAAAAAA (n)
EEEE 0000000 CCCCCCC 0000000 AAAAAAA (n & M[s])
0000 DDDDDDD 0000000 BBBBBBB ((n & s) >> & M[s])
--------------------------------------------------------------------------(+)
EEEE 000000F FFFFFFF 000000G GGGGGGG (m = (n & M[s]) + ((n >> s) & M[s]))
```
2. 接著要加總 `F FFFFFFFF` 及 `G GGGGGGGG`。`(m >> 14) + (m & 0x3fff)`。像這樣:
```c
EEEE 000000F FFFFFFF (m >> 14)
000000G GGGGGGG (m & 0x3fff)
--------------------------------------------------------------------------(+)
EEEE 00000HH HHHHHHH (m = (m >> 14) + (m & 0x3fff))
```
3. 然後進行一個觀察:如果要繼續這樣加總,直到有效位數的寬度比 7 小,那麼只要做 `m = (m >> 7) + (m & 0x7f)` 3 次就好。
4. 可以把 2 ~ 3 用到的 mask 跟位移量都記起來:
```c
int Q[] = {14, 7, 7, 7};
int R[] = {0x00003fff, 0x0000007f, 0x0000007f, 0x0000007f};
```
然後用一個迴圈來做:
```c
for (const unsigned int * q = &Q[0], * r = &R[0]; m > d; q++, r++) {
m = (m >> *q) + (m & *r);
}
```
接著就問:那可不可以從 `s = 0` 一直到 `s = 32` 的所有狀況,都做上面的討論,列舉出每一步需要的位移量,以及需要的 `mask`。然後就可以如法炮製?這個就是裡面程式碼的做法:
```c=
static const unsigned int M[] = {
0x00000000, 0x55555555, 0x33333333, 0xc71c71c7,
0x0f0f0f0f, 0xc1f07c1f, 0x3f03f03f, 0xf01fc07f,
0x00ff00ff, 0x07fc01ff, 0x3ff003ff, 0xffc007ff,
0xff000fff, 0xfc001fff, 0xf0003fff, 0xc0007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff
};
static const unsigned int Q[][6] = {
{ 0, 0, 0, 0, 0, 0}, {16, 8, 4, 2, 1, 1}, {16, 8, 4, 2, 2, 2},
{15, 6, 3, 3, 3, 3}, {16, 8, 4, 4, 4, 4}, {15, 5, 5, 5, 5, 5},
{12, 6, 6, 6 , 6, 6}, {14, 7, 7, 7, 7, 7}, {16, 8, 8, 8, 8, 8},
{ 9, 9, 9, 9, 9, 9}, {10, 10, 10, 10, 10, 10}, {11, 11, 11, 11, 11, 11},
{12, 12, 12, 12, 12, 12}, {13, 13, 13, 13, 13, 13}, {14, 14, 14, 14, 14, 14},
{15, 15, 15, 15, 15, 15}, {16, 16, 16, 16, 16, 16}, {17, 17, 17, 17, 17, 17},
{18, 18, 18, 18, 18, 18}, {19, 19, 19, 19, 19, 19}, {20, 20, 20, 20, 20, 20},
{21, 21, 21, 21, 21, 21}, {22, 22, 22, 22, 22, 22}, {23, 23, 23, 23, 23, 23},
{24, 24, 24, 24, 24, 24}, {25, 25, 25, 25, 25, 25}, {26, 26, 26, 26, 26, 26},
{27, 27, 27, 27, 27, 27}, {28, 28, 28, 28, 28, 28}, {29, 29, 29, 29, 29, 29},
{30, 30, 30, 30, 30, 30}, {31, 31, 31, 31, 31, 31}
};
static const unsigned int R[][6] = {
{0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000ffff, 0x000000ff, 0x0000000f, 0x00000003, 0x00000001, 0x00000001},
{0x0000ffff, 0x000000ff, 0x0000000f, 0x00000003, 0x00000003, 0x00000003},
{0x00007fff, 0x0000003f, 0x00000007, 0x00000007, 0x00000007, 0x00000007},
{0x0000ffff, 0x000000ff, 0x0000000f, 0x0000000f, 0x0000000f, 0x0000000f},
{0x00007fff, 0x0000001f, 0x0000001f, 0x0000001f, 0x0000001f, 0x0000001f},
{0x00000fff, 0x0000003f, 0x0000003f, 0x0000003f, 0x0000003f, 0x0000003f},
{0x00003fff, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f},
{0x0000ffff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff, 0x000000ff},
{0x000001ff, 0x000001ff, 0x000001ff, 0x000001ff, 0x000001ff, 0x000001ff},
{0x000003ff, 0x000003ff, 0x000003ff, 0x000003ff, 0x000003ff, 0x000003ff},
{0x000007ff, 0x000007ff, 0x000007ff, 0x000007ff, 0x000007ff, 0x000007ff},
{0x00000fff, 0x00000fff, 0x00000fff, 0x00000fff, 0x00000fff, 0x00000fff},
{0x00001fff, 0x00001fff, 0x00001fff, 0x00001fff, 0x00001fff, 0x00001fff},
{0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff},
{0x00007fff, 0x00007fff, 0x00007fff, 0x00007fff, 0x00007fff, 0x00007fff},
{0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff},
{0x0001ffff, 0x0001ffff, 0x0001ffff, 0x0001ffff, 0x0001ffff, 0x0001ffff},
{0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff},
{0x0007ffff, 0x0007ffff, 0x0007ffff, 0x0007ffff, 0x0007ffff, 0x0007ffff},
{0x000fffff, 0x000fffff, 0x000fffff, 0x000fffff, 0x000fffff, 0x000fffff},
{0x001fffff, 0x001fffff, 0x001fffff, 0x001fffff, 0x001fffff, 0x001fffff},
{0x003fffff, 0x003fffff, 0x003fffff, 0x003fffff, 0x003fffff, 0x003fffff},
{0x007fffff, 0x007fffff, 0x007fffff, 0x007fffff, 0x007fffff, 0x007fffff},
{0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff},
{0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff},
{0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff},
{0x07ffffff, 0x07ffffff, 0x07ffffff, 0x07ffffff, 0x07ffffff, 0x07ffffff},
{0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff},
{0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff, 0x1fffffff},
{0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff},
{0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}
};
unsigned int n; // numerator
const unsigned int s; // s > 0
const unsigned int d = (1 << s) - 1;
unsigned int m; // n % d goes here.
m = (n & M[s]) + ((n >> s) & M[s]);
for (const unsigned int * q = &Q[s][0], * r = &R[s][0]; m > d; q++, r++) {
m = (m >> *q) + (m & *r);
}
m = m == d ? 0 : m;
```
這個表看起來很恐怖,不過大致上就是依照 `s` 的數值,枚舉上面 2. ~ 3. 步所需要的位移量 (`M`) 跟 mask (`R`)。所以可以發現: `M[i][j]` 的數字是多少,`R[i][j]` 對應的 mask 就是「最低的 `M[i][j]` 位元是 `1`,而這以上的位元都是 `0`」的 mask。