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

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


当前回答

默认情况下,Java的int和long数学在溢出和下溢时默默地环绕。(根据JLS 4.2.2,对其他整数类型的整数操作是通过首先将操作数提升为int或long来执行的。)

从Java 8开始,Java .lang. math为执行命名操作的int和long参数提供了addExact、subtractExact、multiplyExact、incrementExact、decrementExact和negateExact静态方法,并在溢出时抛出arithmeexception。(没有divideExact方法——您必须自己检查一个特殊情况(MIN_VALUE / -1)。)

从Java 8开始,Java .lang. math还提供了toIntExact来将long类型转换为int类型,如果long类型的值不适合int类型,则抛出arithmeexception。这对于例如使用未检查的long math计算int的和,然后在最后使用toIntExact强制转换为int很有用(但要注意不要让你的和溢出)。

If you're still using an older version of Java, Google Guava provides IntMath and LongMath static methods for checked addition, subtraction, multiplication and exponentiation (throwing on overflow). These classes also provide methods to compute factorials and binomial coefficients that return MAX_VALUE on overflow (which is less convenient to check). Guava's primitive utility classes, SignedBytes, UnsignedBytes, Shorts and Ints, provide checkedCast methods for narrowing larger types (throwing IllegalArgumentException on under/overflow, not ArithmeticException), as well as saturatingCast methods that return MIN_VALUE or MAX_VALUE on overflow.

其他回答

它环绕着。

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。

有一些库提供安全的算术操作,用于检查整数溢出/下溢。例如,Guava的IntMath。checkedAdd(int a, int b)返回a和b的和,前提是它没有溢出,如果a + b在有符号int算术中溢出,则抛出arithmeexception。

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

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;
}

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

static final int safeAdd(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE - right
                : left < Integer.MIN_VALUE - right) {
    throw new ArithmeticException("Integer overflow");
  }
  return left + right;
}

static final int safeSubtract(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left < Integer.MIN_VALUE + right
                : left > Integer.MAX_VALUE + right) {
    throw new ArithmeticException("Integer overflow");
  }
  return left - right;
}

static final int safeMultiply(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE/right
                  || left < Integer.MIN_VALUE/right
                : (right < -1 ? left > Integer.MIN_VALUE/right
                                || left < Integer.MAX_VALUE/right
                              : right == -1
                                && left == Integer.MIN_VALUE) ) {
    throw new ArithmeticException("Integer overflow");
  }
  return left * right;
}

static final int safeDivide(int left, int right)
                 throws ArithmeticException {
  if ((left == Integer.MIN_VALUE) && (right == -1)) {
    throw new ArithmeticException("Integer overflow");
  }
  return left / right;
}

static final int safeNegate(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return -a;
}
static final int safeAbs(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return Math.abs(a);
}

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

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

}
System.out.println(res);

会产生:

0

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