30

可能重复:
如何检查在 Java 中将两个数字相乘是否会导致溢出?

假设我有一个 Java 类方法,它使用*+操作。

int foo(int a, int b) {
  ... // 一些带有 + 和 * 的计算
}

如何确保不会发生溢出foo

我想我可以使用BigDecimal或替换所有 + 和 * 为“包装器”,例如:

整数总和(int a,int b){
   诠释 c = a + b;
   如果 (a > 0 && b > 0 && c < 0)
     抛出新的 MyOverfowException(a, b)
   返回 c;
}

int prod(int a,int b){
   诠释 c = a * b;
   如果 (a > 0 && b > 0 && c < 0)
     抛出新的 MyOverfowException(a, b)
   返回 c;
}

有没有更好的方法来确保intJava 方法中不发生溢出?

4

4 回答 4

23

检查溢出的一种方法是将操作数提升为更大的类型(原始操作数位长度的两倍)然后执行操作,然后查看结果值对于原始类型是否太大,例如

int sum(int a, int b) {
    long r = (long)a + b;
    if (r >>> 32 != 0) {    // no sign extension
        throw new MyOverflowException(a, b);
    }
    return (int)r;
}

如果您的原始类型是 a long,则必须使用BigInteger较大的类型。

于 2012-09-01T09:41:41.830 回答
20

从工程的角度来看,这是一个难题。

安全编码网站建议:

  • 使用先决条件;即范围检查输入,使溢出是不可能的,
  • 使用下一个更大的原始整数类型执行每个单独的算术运算并显式检查溢出,或
  • 使用大整数。

Dobbs 博士的这篇文章建议创建一个原始算术方法库,该库通过显式溢出检查来执行每个原始操作。(您可以将其视为上述第 2 点的实现。)但作者进一步建议您使用字节码重写来替换算术字节码,并调用包含溢出检查的等效方法。

不幸的是,没有办法在 Java 中本地启用溢出检查。(但同样适用于许多其他语言;例如 C、C++ ...)

于 2012-09-01T09:45:12.013 回答
6

Sum:检查b是否大于你可以存储在int中的最大值减去a的值的差。如果 a 和/或 b 可以为负数,您必须 (i) 小心不要让差异检查已经溢出,并且 (ii) 对最小值执行类似的检查。

产品:那更难。我会将整数拆分为两个半长整数(即,如果 int 是 32 位,则使用位掩码和移位将其拆分为两个 16 位数字)。然后做乘法,然后看结果是否适合32位。

在您不想简单地采取long临时结果的条件下的一切。

于 2012-09-01T09:41:09.380 回答
3

假设 a 和 b 都是正数或负数,如果 a + b 的符号与 a 和 b 的符号不相等,则会发生溢出。您可以使用此规则来判断是否发生溢出并抛出异常。当你发现这个异常时,你可以按照前面答案中提到的方法来处理它。另一种方法是使用不会溢出的最大范围类型进行操作。您可以将 long 用于整数之间的运算。

于 2012-09-01T09:59:30.503 回答