【问题标题】:Perform integer division using multiplication [duplicate]使用乘法执行整数除法[重复]
【发布时间】:2015-08-27 16:52:26
【问题描述】:

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

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

例如,除以 9:

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

除以 3 将使用与 0x55555555 + 1 的乘法,等等。

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

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

【问题讨论】:

  • 乘以的常数是倒数的近似值。随机的 +/- 1 在这里和那里是为了确保它总是正确地“四舍五入”。可以通过数学方法或通过对所有分子的强力测试来证明特定方法的正确性。 (对于 32 位,这是完全可行的。)
  • @Mysticial:这看起来像是对我的回答。
  • @ScottHunter 也许稍后我下班的时候。我没有足够的工具来给出全面的答案。
  • @Mysticial:你写的评论看起来比我见过的很多答案(还有一些我写的)要好。但我想这就是一个人获得 200K+ 代表的方式。

标签: assembly optimization bit-manipulation division multiplication


【解决方案1】:

该方法称为“不变量乘除法”。

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

所以而不是计算:

N / D = Q

你可以这样做:

N * (1/D) = Q

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

从根本上说,倒数是不精确的,除非 D 是 2 的幂。所以会有一些舍入误差。您看到的+1 用于纠正舍入错误。


最常见的例子是除以 3:

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

0xaaaaaaab = 2^33 / 3 + 1.

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

【讨论】:

  • 规范参考是:T. Granlund 和 PL Montgomery,“使用乘法除以不变整数”,94 年 SIGPLAN 编程语言设计和实现会议论文集 ,1994 年,第 61-72 页。
  • 附加的更新参考:N. Möller 和 T. Granlund,“通过不变整数改进除法”,IEEE Transactions on Computers,第一卷。 60,没有。 2,第 165-175 页,2011 年 2 月。
  • 你的概括和证明是错误的。此外,除以 3 的 0x55555556 仅适用于有符号范围,即。最多 2^31。
  • @Jester 我想我数学很烂。我已经删除了答案的那一部分。
  • @StéphaneGourichon - 除以 7 和其他一些值需要特殊处理。 “精确”倒数需要比整数中的位数多 1 位。这是使用 32 位值和 5 指令序列 (mul, sub, shift right 1, add, shift right ...) 而不是 2 指令序列 (mul, shift right ...) 来处理的。这在prior question 中有解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 2013-10-30
  • 2023-03-25
  • 2020-05-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
相关资源
最近更新 更多