【问题标题】:Avoiding arithmetic overflow避免算术溢出
【发布时间】:2012-02-26 17:24:38
【问题描述】:

假设您有两个大于2^32(且小于2^63)的正longab,以及一个长整数c。 在java和\或c中,执行诸如

之类的操作的最佳方式是什么
(a*b)%c

同时避免算术溢出。

编辑: c 在2^34 附近,有时a 和b 都在2^32c 之间...

对于我所处的具体情况,我终于避免使用BigInteger。事实上,有可能知道ab 的一个除数(并非总是如此),所以我会使用算术属性modulo 对我有利。

【问题讨论】:

标签: java c math precision


【解决方案1】:

假设一切都是正数,那么您可以使用以下数学恒等式:

(a*b)%c == ((a%c) * (b%c)) % c

当然,这仍然不能消除溢出的可能性。

完全避免该问题的最简单方法是使用大整数库。

【讨论】:

  • 是的,这就是我所拥有的。 c 在 2^34 左右,有时 a 和 b 都在 2^32 和 c 之间...
  • 值得指出的是,如果c <= sqrt(LONG_MAX),右侧是安全的。
【解决方案2】:

您可以比@Oli Charlesworth 在他的(好)答案中建议的更进一步。您可以分解因子ab(并非所有主要因子都需要,部分分解可能就足够了)并在乘法的任何中间结果中执行模数。虽然这可能比使用 bignum 更昂贵,因为它涉及很多部门,而且成本很高。

【讨论】:

  • 谢谢。其实我就是这么做的。通常这种分解代价高昂,但我意识到在我的情况下它不是(由于首先生成数字的方式)。
【解决方案3】:

在 Java 中我会使用 BigInteger:

BigInteger bd = BigInteger.valueOf(2).pow(33);
System.out.println(bd.multiply(bd).remainder(BigInteger.valueOf(2).pow(34).add(BigInteger.valueOf(1))));

【讨论】:

  • ...BigDecimal,而不是BigInteger?
【解决方案4】:

据我所知,如果没有更高精度的算术,就无法解决您的问题,至少 LLVM 的优化器同意。

如果 128 位数学在本机上不可用,则需要使用通用大整数库,或者从不太通用的实现中获取所需的位,例如 GnuCash 中的 Math128

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-06
    相关资源
    最近更新 更多