避免整数乘法后除法溢出

Avoiding overflow in integer multiplication followed by division

本文关键字:除法 溢出 整数      更新时间:2023-10-16

我有两个积分变量ab和一个常数s resp。d。我需要计算(a*b)>>s resp的值。a*b/d。问题是,乘法运算可能会溢出,即使a*b/d可以适合给定的整型,最终结果也不会正确。

如何有效地解决这个问题?直接的解决方案是将变量ab展开为更大的整型,但可能没有更大的整型。有更好的方法来解决这个问题吗?

如果没有更大的类型,则需要找到一个大int风格的库,或者使用长乘法手动处理它。

例如,假设ab是16位的。然后您可以将它们重写为a = (1<<8)*aH + aLb = (1<<8)*bH + bL(其中所有单独的组件都是8位数字)。那么您就知道总体结果将是:

(a*b) = (1<<16)*aH*bH
      + (1<<8)*aH*bL
      + (1<<8)*aL*bH
      + aL*bL

这4个组件中的每一个都将适合16位寄存器。现在,你可以在每个单独的组件上执行右移,注意适当地处理进号。

我还没有详尽地测试过这个,但是您可以先做除法,然后考虑余数,而牺牲额外的操作吗?由于d是2的幂,所以所有的除法都可以简化为位运算。

例如,始终假设a > b(您希望先除较大的数字)。那么a * b / d = ((a / d) * b) + (((a % d) * b) / d)

如果较大的类型只有64位,那么直接的解决方案很可能会产生高效的代码。在x86 cpu上,两个32位数字的任何乘法都会在另一个寄存器中造成溢出。因此,如果您的编译器理解这一点,它可以为Int64 result=(Int64)a*(Int64)b生成高效的代码。

我在c#中遇到了同样的问题,编译器生成了相当好的代码。c++编译器通常比。net JIT生成更好的代码。

我建议将代码转换为较大的类型,然后检查生成的汇编代码,以检查它是否良好。

在某些情况下(历史上使用选定常量的LCG随机数生成器),对于a和d的某些值,可以执行您想要的操作。

这被称为施拉格方法。div。