8

查看由编译器生成的x86程序集,我注意到(无符号)整数除法有时实现为整数乘法。这些优化似乎遵循形式使用乘法执行整数除法

value/n => (value * ((0xFFFFFFFF/n) + 1))/0x100000000 

例如,通过9执行除法:

12345678/9 = (12345678 * 0x1C71C71D)/0x100000000 

由3所述的分割将利用乘法0x55555555 + 1,等等。

利用mul指令将结果的高位部分存储在edx寄存器中的事实,最终的除法结果可以使用带有魔值的单乘法获得。 (虽然这种优化有时与最后按位移位结合使用)。

我想了解一下这个实际工作原理。这种方法何时有效?为什么必须将1添加到我们的“幻数”中?

+2

您乘以的常数是倒数的近似值。随机的+/- 1在这里和那里确保它总是正确地“圆化”。证明一种特定的方法是正确的,可以通过数学方法或通过对所有分子进行强力测试来完成。 (对于32位,这是完全可行的。) – Mysticial

+0

@Mysticial:这看起来像是对我的回答。 –

+0

@ScottHunter也许以后当我不工作时。我没有很多工具可以给出全面的答案。 – Mysticial

回答

10

该方法被称为“通过不变乘法除法”。

你看到的常量实际上是倒数的近似值。

因此,而不是计算:

N/D = Q 

你做这样的事情,而不是:

N * (1/D) = Q 

其中1/D是可以预先计算的倒数。

基本上,倒数是不精确的,除非D是2的幂。所以会出现一些舍入误差。您看到的+1可以纠正四舍五入错误。


最常见的例子是除以3:

N/3 = (N * 0xaaaaaaab) >> 33 

哪里0xaaaaaaab = 2^33/3 + 1

这种方法将推广到其他除数。

+1

规范性的参考文献是:T.Glulund和PLMontgomery,“用乘法不变整数除法”,* SIGPLAN '94 Conference on Programming Language Design和执行*,1994年,第61-72页。 – njuffa

+1

其他更新的参考文献:N.Möller和T. Granlund,“不变整数的改进划分”,* IEEE Transactions on Computers *,vol。 60,没有。 2,第165-175页,2011年2月。 – njuffa

+0

你的概括和证明是错误的。除3之外的0x55555556也只适用于符号范围,即。高达2^31。 – Jester