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

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


当前回答

Java对int或长基元类型的整数溢出没有做任何处理,而是忽略正整数和负整数溢出。

这个答案首先描述了整数溢出的原因,给出了一个例子,说明了它是如何发生的,即使是表达式求值中的中间值,然后给出了一些资源的链接,这些资源提供了防止和检测整数溢出的详细技术。

整数算术和表达式导致意外或未检测到的溢出是常见的编程错误。意外或未检测到的整数溢出也是一个众所周知的可利用的安全问题,特别是当它影响数组、堆栈和列表对象时。

溢出可能以正或负的方向发生,其中正或负的值将超过所讨论的基元类型的最大值或最小值。在表达式或操作求值期间,中间值可能发生溢出,并影响最终值在范围内的表达式或操作的结果。

Sometimes negative overflow is mistakenly called underflow. Underflow is what happens when a value would be closer to zero than the representation allows. Underflow occurs in integer arithmetic and is expected. Integer underflow happens when an integer evaluation would be between -1 and 0 or 0 and 1. What would be a fractional result truncates to 0. This is normal and expected with integer arithmetic and not considered an error. However, it can lead to code throwing an exception. One example is an "ArithmeticException: / by zero" exception if the result of integer underflow is used as a divisor in an expression.

考虑下面的代码:

int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;

这将导致x被赋值为0,并且bigValue / x的后续求值会抛出一个异常,“ArithmeticException: / by zero”(即除以0),而不是将y赋值为2。

x的预期结果是858,993,458,小于最大int值2,147,483,647。但是,计算Integer的中间结果。MAX_Value * 2,将是4,294,967,294,它超过了最大int值,并根据2s补全整数表示为-2。然后-2 / 5的结果是0,赋值给x。

将计算x的表达式重新排列为在求值时先除后乘的表达式,如下代码:

int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;

结果x被赋值为858,993,458,y被赋值为2,这是预期的。

bigValue / 5的中间结果是429,496,729,它没有超过int类型的最大值。随后计算429,496,729 * 2不会超过int的最大值,预期结果被分配给x。然后对y的计算不除零。对x和y的求值按预期工作。

Java integer values are stored as and behave in accordance with 2s complement signed integer representations. When a resulting value would be larger or smaller than the maximum or minimum integer values, a 2's complement integer value results instead. In situations not expressly designed to use 2s complement behavior, which is most ordinary integer arithmetic situations, the resulting 2s complement value will cause a programming logic or computation error as was shown in the example above. An excellent Wikipedia article describes 2s compliment binary integers here: Two's complement - Wikipedia

有一些技术可以避免意外的整数溢出。技术可以分为使用前置条件测试、向上转换和BigInteger。

前置条件测试包括检查进入算术运算或表达式的值,以确保这些值不会发生溢出。编程和设计将需要创建测试,以确保输入值不会导致溢出,然后确定如果发生导致溢出的输入值该怎么办。

Upcasting comprises using a larger primitive type to perform the arithmetic operation or expression and then determining if the resulting value is beyond the maximum or minimum values for an integer. Even with upcasting, it is still possible that the value or some intermediate value in an operation or expression will be beyond the maximum or minimum values for the upcast type and cause overflow, which will also not be detected and will cause unexpected and undesired results. Through analysis or pre-conditions, it may be possible to prevent overflow with upcasting when prevention without upcasting is not possible or practical. If the integers in question are already long primitive types, then upcasting is not possible with primitive types in Java.

The BigInteger technique comprises using BigInteger for the arithmetic operation or expression using library methods that use BigInteger. BigInteger does not overflow. It will use all available memory, if necessary. Its arithmetic methods are normally only slightly less efficient than integer operations. It is still possible that a result using BigInteger may be beyond the maximum or minimum values for an integer, however, overflow will not occur in the arithmetic leading to the result. Programming and design will still need to determine what to do if a BigInteger result is beyond the maximum or minimum values for the desired primitive result type, e.g., int or long.

卡内基梅隆软件工程研究所的CERT程序和Oracle已经为安全Java编程创建了一套标准。这些标准包括防止和检测整数溢出的技术。该标准作为一个免费的在线资源发布:CERT Oracle Java安全编码标准

该标准的部分描述并包含了防止或检测整数溢出的编码技术的实际示例:NUM00-J。检测或防止整数溢出

图书形式和PDF形式的CERT Oracle Java安全编码标准也可用。

其他回答

我想这应该没问题。

static boolean addWillOverFlow(int a, int b) {
    return (Integer.signum(a) == Integer.signum(b)) && 
            (Integer.signum(a) != Integer.signum(a+b)); 
}

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

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

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

它环绕着。

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。

Java对int或长基元类型的整数溢出没有做任何处理,而是忽略正整数和负整数溢出。

这个答案首先描述了整数溢出的原因,给出了一个例子,说明了它是如何发生的,即使是表达式求值中的中间值,然后给出了一些资源的链接,这些资源提供了防止和检测整数溢出的详细技术。

整数算术和表达式导致意外或未检测到的溢出是常见的编程错误。意外或未检测到的整数溢出也是一个众所周知的可利用的安全问题,特别是当它影响数组、堆栈和列表对象时。

溢出可能以正或负的方向发生,其中正或负的值将超过所讨论的基元类型的最大值或最小值。在表达式或操作求值期间,中间值可能发生溢出,并影响最终值在范围内的表达式或操作的结果。

Sometimes negative overflow is mistakenly called underflow. Underflow is what happens when a value would be closer to zero than the representation allows. Underflow occurs in integer arithmetic and is expected. Integer underflow happens when an integer evaluation would be between -1 and 0 or 0 and 1. What would be a fractional result truncates to 0. This is normal and expected with integer arithmetic and not considered an error. However, it can lead to code throwing an exception. One example is an "ArithmeticException: / by zero" exception if the result of integer underflow is used as a divisor in an expression.

考虑下面的代码:

int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;

这将导致x被赋值为0,并且bigValue / x的后续求值会抛出一个异常,“ArithmeticException: / by zero”(即除以0),而不是将y赋值为2。

x的预期结果是858,993,458,小于最大int值2,147,483,647。但是,计算Integer的中间结果。MAX_Value * 2,将是4,294,967,294,它超过了最大int值,并根据2s补全整数表示为-2。然后-2 / 5的结果是0,赋值给x。

将计算x的表达式重新排列为在求值时先除后乘的表达式,如下代码:

int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;

结果x被赋值为858,993,458,y被赋值为2,这是预期的。

bigValue / 5的中间结果是429,496,729,它没有超过int类型的最大值。随后计算429,496,729 * 2不会超过int的最大值,预期结果被分配给x。然后对y的计算不除零。对x和y的求值按预期工作。

Java integer values are stored as and behave in accordance with 2s complement signed integer representations. When a resulting value would be larger or smaller than the maximum or minimum integer values, a 2's complement integer value results instead. In situations not expressly designed to use 2s complement behavior, which is most ordinary integer arithmetic situations, the resulting 2s complement value will cause a programming logic or computation error as was shown in the example above. An excellent Wikipedia article describes 2s compliment binary integers here: Two's complement - Wikipedia

有一些技术可以避免意外的整数溢出。技术可以分为使用前置条件测试、向上转换和BigInteger。

前置条件测试包括检查进入算术运算或表达式的值,以确保这些值不会发生溢出。编程和设计将需要创建测试,以确保输入值不会导致溢出,然后确定如果发生导致溢出的输入值该怎么办。

Upcasting comprises using a larger primitive type to perform the arithmetic operation or expression and then determining if the resulting value is beyond the maximum or minimum values for an integer. Even with upcasting, it is still possible that the value or some intermediate value in an operation or expression will be beyond the maximum or minimum values for the upcast type and cause overflow, which will also not be detected and will cause unexpected and undesired results. Through analysis or pre-conditions, it may be possible to prevent overflow with upcasting when prevention without upcasting is not possible or practical. If the integers in question are already long primitive types, then upcasting is not possible with primitive types in Java.

The BigInteger technique comprises using BigInteger for the arithmetic operation or expression using library methods that use BigInteger. BigInteger does not overflow. It will use all available memory, if necessary. Its arithmetic methods are normally only slightly less efficient than integer operations. It is still possible that a result using BigInteger may be beyond the maximum or minimum values for an integer, however, overflow will not occur in the arithmetic leading to the result. Programming and design will still need to determine what to do if a BigInteger result is beyond the maximum or minimum values for the desired primitive result type, e.g., int or long.

卡内基梅隆软件工程研究所的CERT程序和Oracle已经为安全Java编程创建了一套标准。这些标准包括防止和检测整数溢出的技术。该标准作为一个免费的在线资源发布:CERT Oracle Java安全编码标准

该标准的部分描述并包含了防止或检测整数溢出的编码技术的实际示例:NUM00-J。检测或防止整数溢出

图书形式和PDF形式的CERT Oracle Java安全编码标准也可用。