最近看到一段汇编,使用乘法和位运算求整数除以 3
的结果,里面用到了一个魔数 0xaaaaaaab
,刚看时不理解,于是在网上查了一下,弄明白后分享给大家。
首先,假设有一个 32
位无符号整数 x
,现在需要计算 x / 3
的值。
再假设 x = n * 3 + r
,且 0 <= r < 3
,则 n
就为 x / 3
的值,再令 y = x * 0xaaaaaaab
。
将 x
展开可得,y = n * 3 * 0xaaaaaaab + r * 0xaaaaaaab
。
已知,3 * 0xaaaaaaab = 2^33 + 1
,则
y = n * (2^33 + 1) + r * 0xaaaaaaab
。进一步,y = n * 2^33 + n + r * 0xaaaaaaab
。
由于 n
是 x / 3
的值,且 x < 2^32
,因此 n < 2^32 / 3
,则 n + r * 0xaaaaaaab < 2^33
。(这是因为 r
最大取 2
,且 n
远小于 0xaaaaaaab
,所以该不等式成立。)
所以,y >> 33
就是 n
的值,所以 n = (x * 0xaaaaaaab >> 33)
,即 x / 3
的值。
测试代码如下所示:
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
int div3(uint32_t x)
{
return x * 0xaaaaaaabULL >> 33;
}
int main(void)
{
bool matched = true;
for (uint32_t i = 0; i <= 31; ++i) {
uint32_t x = ((1 << i) + i);
printf("x = %u, div3(x) = %d, x / 3 = %d\n", x, div3(x), x / 3);
if (div3(x) != x / 3)
matched = false;
}
if (!matched)
printf("div3(x) not matched x / 3!!!\n");
else
printf("div3(x) matched x / 3!\n");
return 0;
}
程序运行结果如下所示:
$ gcc -o main main.c
$ ./main
x = 1, div3(x) = 0, x / 3 = 0
x = 3, div3(x) = 1, x / 3 = 1
x = 6, div3(x) = 2, x / 3 = 2
x = 11, div3(x) = 3, x / 3 = 3
x = 20, div3(x) = 6, x / 3 = 6
x = 37, div3(x) = 12, x / 3 = 12
x = 70, div3(x) = 23, x / 3 = 23
x = 135, div3(x) = 45, x / 3 = 45
x = 264, div3(x) = 88, x / 3 = 88
x = 521, div3(x) = 173, x / 3 = 173
x = 1034, div3(x) = 344, x / 3 = 344
x = 2059, div3(x) = 686, x / 3 = 686
x = 4108, div3(x) = 1369, x / 3 = 1369
x = 8205, div3(x) = 2735, x / 3 = 2735
x = 16398, div3(x) = 5466, x / 3 = 5466
x = 32783, div3(x) = 10927, x / 3 = 10927
x = 65552, div3(x) = 21850, x / 3 = 21850
x = 131089, div3(x) = 43696, x / 3 = 43696
x = 262162, div3(x) = 87387, x / 3 = 87387
x = 524307, div3(x) = 174769, x / 3 = 174769
x = 1048596, div3(x) = 349532, x / 3 = 349532
x = 2097173, div3(x) = 699057, x / 3 = 699057
x = 4194326, div3(x) = 1398108, x / 3 = 1398108
x = 8388631, div3(x) = 2796210, x / 3 = 2796210
x = 16777240, div3(x) = 5592413, x / 3 = 5592413
x = 33554457, div3(x) = 11184819, x / 3 = 11184819
x = 67108890, div3(x) = 22369630, x / 3 = 22369630
x = 134217755, div3(x) = 44739251, x / 3 = 44739251
x = 268435484, div3(x) = 89478494, x / 3 = 89478494
x = 536870941, div3(x) = 178956980, x / 3 = 178956980
x = 1073741854, div3(x) = 357913951, x / 3 = 357913951
x = 2147483679, div3(x) = 715827893, x / 3 = 715827893
div3(x) matched x / 3!