Assembly 使用乘法执行整数除法

Assembly 使用乘法执行整数除法,assembly,optimization,bit-manipulation,division,multiplication,Assembly,Optimization,Bit Manipulation,Division,Multiplication,查看编译器生成的x86程序集,我注意到(无符号)整数除法有时实现为整数乘法。这些优化似乎遵循以下形式 value / n => (value * ((0xFFFFFFFF / n) + 1)) / 0x100000000 例如,执行9除法: 12345678 / 9 = (12345678 * 0x1C71C71D) / 0x100000000 除以3将使用与0x5555+1的乘法,依此类推 利用mul指令将结果的高位存储在edx寄存器中这一事实,可以使用与幻数的单次乘法获得除法的最终

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

value / n => (value * ((0xFFFFFFFF / n) + 1)) / 0x100000000
例如,执行9除法:

12345678 / 9 = (12345678 * 0x1C71C71D) / 0x100000000
除以3将使用与
0x5555+1的乘法,依此类推

利用
mul
指令将结果的高位存储在
edx
寄存器中这一事实,可以使用与幻数的单次乘法获得除法的最终结果。(尽管这种优化有时与结尾的位移位结合使用。)

我想了解一下这实际上是如何工作的。这种方法何时有效?为什么必须在“幻数”中加1呢?

这种方法叫做“不变乘法除法”

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

因此,与计算相比:

N / D = Q
您可以这样做:

N * (1/D) = Q
其中,
1/D
是可预计算的倒数

从根本上说,除非
D
是二的幂,否则倒数是不精确的。因此,将涉及一些舍入误差。您看到的
+1
用于纠正舍入错误


最常见的例子是除以3:

N / 3 = (N * 0xaaaaaaab) >> 33
其中
0xaaab=2^33/3+1


此方法将推广到其他除数。

您乘以的常数是倒数的近似值。随机+/-1在这里和那里,以确保它总是“四舍五入”正确。证明某一特定方法是正确的,可以通过数学方法,也可以通过对所有分子进行蛮力测试来完成。(对于32位来说,这是完全可行的。)@Mysticial:这对我来说似乎是一个答案。@ScottHunter也许等我下班后再说。我没有足够的工具在这里给出一个全面的答案。@Mysticial:你作为评论写的东西看起来比我看到的很多答案(还有一些我写的)都要好。但我想这就是人们获得200K+代表的方式。标准参考文献是:T.Granlund和P.L.Montgomery,“使用乘法除以不变整数”,发表于1994年SIGPLAN'94编程语言设计与实现会议记录,第61-72页。另外,最近的参考文献:N.Möller和T.Granlund,“改进的不变整数除法,”《计算机上的IEEE交易》,第60卷,第2期,第165-175页,2011年2月。你的概括和证明是错误的。此外,被3除的0x555556只适用于有符号范围,即2^31。@Jester我想我的数学很烂。我已经删除了答案的这一部分。@StéphaneGourichon-被7除和其他一些值需要特殊处理处理。“精确”倒数需要比整数位数多1位。这是使用32位值和5指令序列(mul、sub、shift right 1、add、shift right…)而不是2指令序列(mul、shift right…)来处理的。这在中进行了解释。