请参考我的回答:
Is this expression correct in C preprocessor
我在这个领域有点不熟悉,正在尝试理解这种特定的优化方法。
正如答案中提到的那样,gcc将对除以7的整数除法进行优化:
mov edx, -1840700269
mov eax, edi
imul edx
lea eax, [rdx+rdi]
sar eax, 2
sar edi, 31
sub eax, edi
转换回C语言的代码如下:
int32_t divideBySeven(int32_t num) {
int32_t temp = ((int64_t)num * -015555555555) >> 32;
temp = (temp + num) >> 2;
return (temp - (num >> 31));
}
让我们来看一下第一部分:
int32_t temp = ((int64_t)num * -015555555555) >> 32;
为什么是这个数字?
好的,我们来计算一下2的64次方除以7得到的结果。
2^64 / 7 = 2635249153387078802.28571428571428571429
看起来很混乱,如果我们将它转换成八进制会怎么样?
0222222222222222222222.22222222222222222222222
这是一个非常漂亮的重复模式,肯定不是巧合。我们记得7是0b111
,我们知道当我们除以99时,倾向于在十进制中获得重复模式。因此,当我们除以7时,我们得到一个八进制的重复模式是有道理的。
那么我们的数字从哪里来?
(int32_t)-1840700269
与(uint_32t)2454267027
相同
* 7 = 17179869189
最后,17179869184是2^34
这意味着17179869189是最接近7·2^34的倍数的数。换句话说,2454267027是最大的适合uint32_t
的数,当它乘以7时,非常接近于2的幂次方
这个数字的八进制表示是什么?
0222222222223
为什么这很重要?我们想要除以7。 这个数字是2的34次方/ 7...大约如此。 因此,如果我们乘以它,然后左移34次,我们应该得到一个非常接近精确数字的数字。
最后两行看起来像是为弥补近似误差而设计的。
也许在这个领域有更多知识和/或专业知识的人可以发表意见。
>>> magic = 2454267027
>>> def div7(a):
... if (int(magic * a >> 34) != a // 7):
... return 0
... return 1
...
>>> for a in xrange(2**31, 2**32):
... if (not div7(a)):
... print "%s fails" % a
...
故障开始于3435973841,有趣的是它等于0b11001100110011001100110011010001。
为什么近似会失败以及修补程序如何解决这个问题,我不太清楚。有人知道更多的魔法原理吗?
gmp-impl.h
中的udiv_qrnnd_preinv
)。 - Brett Hale