我有两个整数变量a
和b
以及一个常量s
resp。 d
。我需要计算(a*b)>>s
resp的值。 a*b/d
。问题是乘法可能会溢出,即使a*b/d
适合给定的整数类型,最终结果也不正确。
如何有效地解决这个问题?直接的解决方案是将变量a
或b
扩展为更大的整数类型,但可能没有更大的整数类型。有没有更好的方法来解决这个问题?
答案 0 :(得分:13)
如果没有更大的类型,您将需要找到一个big-int样式库,或者使用长乘法手动处理它。
例如,假设a
和b
是16位。然后,您可以将它们重写为a = (1<<8)*aH + aL
和b = (1<<8)*bH + bL
(其中所有单个组件都是8位数字)。然后你知道整体结果将是:
(a*b) = (1<<16)*aH*bH
+ (1<<8)*aH*bL
+ (1<<8)*aL*bH
+ aL*bL
这4个组件中的每一个都适合16位寄存器。您现在可以执行以对每个组成部分进行右移,小心处理适当的工作。
答案 1 :(得分:4)
如果较大的类型只是64位,则直接解决方案很可能会产生有效的代码。在x86 CPU上,两个32位数的任何乘法都会在另一个寄存器中产生溢出。因此,如果编译器理解这一点,它可以为Int64 result=(Int64)a*(Int64)b
生成有效的代码。
我在C#中遇到了同样的问题,编译器生成了相当不错的代码。 C ++编译器通常会创建比.net JIT更好的代码。
我建议将带有强制转换的代码编写到较大的类型中,然后检查生成的汇编代码以检查它是否正常。
答案 2 :(得分:3)
我没有详尽地对此进行过测试,但是你可以先进行分组,然后考虑剩下的部分而不考虑额外的操作吗?由于d
是2的幂,因此所有除法都可以简化为按位运算。
例如,始终假设a > b
(您希望先将较大的数字除以)。然后a * b / d
= ((a / d) * b) + (((a % d) * b) / d)
答案 3 :(得分:0)
在某些情况下(历史上具有所选常数的LCG随机数生成器),对于a和d的某些值,可以执行您想要的操作。
这称为Schrage的方法,参见例如。 there