Java如何处理整数下溢和溢出?

在此基础上,你将如何检查/测试这种情况的发生?


当前回答

它环绕着。

e.g:

public class Test {

    public static void main(String[] args) {
        int i = Integer.MAX_VALUE;
        int j = Integer.MIN_VALUE;

        System.out.println(i+1);
        System.out.println(j-1);
    }
}

打印

-2147483648
2147483647

从java8开始,java.lang.Math包就有了addExact()和multiplyExact()这样的方法,它们会在发生溢出时抛出一个ArithmeticException。

其他回答

它什么都不做——只是发生了under/overflow。

“-1”是溢出计算的结果,与任何其他信息产生的“-1”没有区别。因此,您不能通过某些状态或仅仅检查一个值来判断它是否溢出。

但是为了避免溢出,你可以聪明地进行计算,如果这很重要的话,或者至少知道什么时候会发生溢出。你的情况如何?

如果溢出,则返回最小值并从那里继续。如果它溢出,它回到最大值,并从那里继续。

您可以事先检查如下:

public static boolean willAdditionOverflow(int left, int right) {
    if (right < 0 && right != Integer.MIN_VALUE) {
        return willSubtractionOverflow(left, -right);
    } else {
        return (~(left ^ right) & (left ^ (left + right))) < 0;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    if (right < 0) {
        return willAdditionOverflow(left, -right);
    } else {
        return ((left ^ right) & (left ^ (left - right))) < 0;
    }
}

(你可以用long替换int来对long执行相同的检查)

如果您认为这种情况可能经常发生,那么可以考虑使用可以存储较大值的数据类型或对象,例如long或java.math.BigInteger。最后一个不会溢出,实际上,可用的JVM内存是限制。


如果您碰巧已经使用了Java8,那么您可以使用新的Math#addExact()和Math#subtractExact()方法,它们将在溢出时抛出一个ArithmeticException。

public static boolean willAdditionOverflow(int left, int right) {
    try {
        Math.addExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    try {
        Math.subtractExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

源代码可以分别在这里和这里找到。

当然,您也可以立即使用它们,而不是将它们隐藏在布尔实用程序方法中。

有一种情况,上面没有提到:

int res = 1;
while (res != 0) {
    res *= 2;

}
System.out.println(res);

会产生:

0

本案例讨论如下: 整数溢出产生零。

我认为你应该使用这样的东西,它被称为Upcasting:

public int multiplyBy2(int x) throws ArithmeticException {
    long result = 2 * (long) x;    
    if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){
        throw new ArithmeticException("Integer overflow");
    }
    return (int) result;
}

你可以在这里进一步阅读: 检测或防止整数溢出

这是相当可靠的来源。

我自己也遇到了这个问题,下面是我的解决方案(包括乘法和加法):

static boolean wouldOverflowOccurwhenMultiplying(int a, int b) {
    // If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow
    if (a == 0 || b == 0) {
        return false;
    } else if (a > 0 && b > 0) { // both positive, non zero
        return a > Integer.MAX_VALUE / b;
    } else if (b < 0 && a < 0) { // both negative, non zero
        return a < Integer.MAX_VALUE / b;
    } else { // exactly one of a,b is negative and one is positive, neither are zero
        if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow.
            return a < Integer.MIN_VALUE / b;
        } else { // a > 0
            return b < Integer.MIN_VALUE / a;
        }
    }
}

boolean wouldOverflowOccurWhenAdding(int a, int b) {
    if (a > 0 && b > 0) {
        return a > Integer.MAX_VALUE - b;
    } else if (a < 0 && b < 0) {
        return a < Integer.MIN_VALUE - b;
    }
    return false;
}

如果有错误或者可以简化,请随意纠正。我已经用乘法法做了一些测试,大部分是边缘情况,但它仍然可能是错误的。