Java 如何处理整数下溢和溢出以及如何检查它?

发布于 2024-09-04 19:17:40 字数 58 浏览 7 评论 0原文

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

在此基础上,您将如何检查/测试这种情况是否正在发生?

How does Java handle integer underflows and overflows?

Leading on from that, how would you check/test that this is occurring?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

鸩远一方 2024-09-11 19:17:40

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

您可以使用 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;
    }
}

您可以用 long 替换 int,对 long 执行相同的检查。

源代码可以找到 这里此处 分别。

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

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

If it overflows, it goes back to the minimum value and continues from there. If it underflows, it goes back to the maximum value and continues from there.

You can make use of the Math#addExact() and Math#subtractExact() methods which will throw an ArithmeticException on overflow.

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

You can substitute int by long to perform the same checks for long.

The source code can be found here and here respectively.

Of course, you could also just use them right away instead of hiding them in a boolean utility method.

If you think that this may occur more than often, then consider using a datatype or object which can store larger values, e.g. long or maybe java.math.BigInteger. The last one doesn't overflow, practically, the available JVM memory is the limit.

美男兮 2024-09-11 19:17:40

好吧,就原始整数类型而言,Java 根本不处理上溢/下溢(对于浮点型和双精度型,行为不同,它将刷新到 +/- 无穷大,正如 IEEE-754 所要求的那样)。

当两个 int 相加时,发生溢出时你不会得到任何指示。检查溢出的一个简单方法是使用下一个更大的类型来实际执行操作并检查结果是否仍在源类型的范围内:

public int addWithOverflowCheck(int a, int b) {
    // the cast of a is required, to make the + work with long precision,
    // if we just added (a + b) the addition would use int precision and
    // the result would be cast to long afterwards!
    long result = ((long) a) + b;
    if (result > Integer.MAX_VALUE) {
         throw new RuntimeException("Overflow occured");
    } else if (result < Integer.MIN_VALUE) {
         throw new RuntimeException("Underflow occured");
    }
    // at this point we can safely cast back to int, we checked before
    // that the value will be withing int's limits
    return (int) result;
}

您将做什么来代替 throw 子句,取决于您的应用程序要求(抛出、刷新到最小/最大或只是记录任何内容)。如果您想检测长操作的溢出,那么您对原语不走运,请改用 BigInteger。


编辑(2014-05-21):由于这个问题似乎被频繁提及,而且我自己也必须解决同样的问题,因此通过 CPU 计算其 V 标志的相同方法来评估溢出条件非常容易。

它基本上是一个布尔表达式,涉及两个操作数的符号以及结果:

/**
 * Add two int's with overflow detection (r = s + d)
 */
public static int add(final int s, final int d) throws ArithmeticException {
    int r = s + d;
    if (((s & d & ~r) | (~s & ~d & r)) < 0)
        throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");    
    return r;
}

在 java 中,将表达式(在 if 中)应用于整个 32 位,并使用 <> 检查结果更简单。 0(这将有效地测试符号位)。对于所有整数基元类型,其原理完全相同,将上述方法中的所有声明更改为 long 使其适用于 long 。

对于较小的类型,由于隐式转换为 int(详细信息请参阅 JLS 位操作),而不是检查 << 0,检查需要显式屏蔽符号位(短操作数为 0x8000,字节操作数为 0x80,适当调整强制转换和参数声明):(

/**
 * Subtract two short's with overflow detection (r = d - s)
 */
public static short sub(final short d, final short s) throws ArithmeticException {
    int r = d - s;
    if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
        throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
    return (short) r;
}

请注意,上面的示例使用了表达式 need for subtract 溢出检测)


那么这些布尔表达式如何/为什么起作用呢?首先,一些逻辑思维表明,只有当两个参数的符号相同时才会发生溢出。因为,如果一个参数为负,一个为正,则(add)的结果必须接近于零,或者在极端情况下,一个参数为零,与另一个参数相同。由于参数本身不能创建溢出条件,因此它们的总和也不能创建溢出。

那么如果两个参数具有相同的符号会发生什么?让我们看一下两者都是正数的情况:添加两个参数,其总和大于 MAX_VALUE 类型,将始终产生负值,因此如果 arg1 + arg2 > 则发生溢出。 MAX_VALUE。现在可能产生的最大值将为 MAX_VALUE + MAX_VALUE(极端情况下两个参数均为 MAX_VALUE)。对于一个字节(示例),这意味着 127 + 127 = 254。查看将两个正值相加所产生的所有值的位表示形式,我们会发现溢出的值(128 到 254)都设置了位 7,而所有不溢出(0 到 127)的位 7(最上面的符号)都被清除。这正是表达式检查的第一部分(右侧):(

if (((s & d & ~r) | (~s & ~d & r)) < 0)

~s & ~d & r) 变为 true,仅当,两个操作数 (s, d) 均为正且结果(r) 为负(该表达式适用于所有 32 位,但我们唯一感兴趣的位是最上面的(符号)位,它由 <0 进行检查)。

现在,如果两个参数均为负数,则它们的总和永远不会比任何参数更接近零,总和必须更接近负无穷大。我们可以产生的最极端值是 MIN_VALUE + MIN_VALUE,它(再次以字节为例)表明,对于任何范围内的值(-1 到 -128),符号位都会被设置,而任何可能的溢出值(-129 到 -256) ) 已清除符号位。因此结果的符号再次揭示了溢出情况。这就是左半部分 (s & d & ~r) 检查两个参数 (s, d) 均为负且结果为正的情况。其逻辑与正例基本相同;当且仅当发生下溢时,由两个负值相加所产生的所有位模式的符号位都会被清除。

Well, as far as primitive integer types go, Java doesnt handle Over/Underflow at all (for float and double the behaviour is different, it will flush to +/- infinity just as IEEE-754 mandates).

When adding two int's, you will get no indication when an overflow occurs. A simple method to check for overflow is to use the next bigger type to actually perform the operation and check if the result is still in range for the source type:

public int addWithOverflowCheck(int a, int b) {
    // the cast of a is required, to make the + work with long precision,
    // if we just added (a + b) the addition would use int precision and
    // the result would be cast to long afterwards!
    long result = ((long) a) + b;
    if (result > Integer.MAX_VALUE) {
         throw new RuntimeException("Overflow occured");
    } else if (result < Integer.MIN_VALUE) {
         throw new RuntimeException("Underflow occured");
    }
    // at this point we can safely cast back to int, we checked before
    // that the value will be withing int's limits
    return (int) result;
}

What you would do in place of the throw clauses, depends on your applications requirements (throw, flush to min/max or just log whatever). If you want to detect overflow on long operations, you're out of luck with primitives, use BigInteger instead.


Edit (2014-05-21): Since this question seems to be referred to quite frequently and I had to solve the same problem myself, its quite easy to evaluate the overflow condition by the same method a CPU would calculate its V flag.

Its basically a boolean expression that involves the sign of both operands as well as the result:

/**
 * Add two int's with overflow detection (r = s + d)
 */
public static int add(final int s, final int d) throws ArithmeticException {
    int r = s + d;
    if (((s & d & ~r) | (~s & ~d & r)) < 0)
        throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");    
    return r;
}

In java its simpler to apply the expression (in the if) to the entire 32 bits, and check the result using < 0 (this will effectively test the sign bit). The principle works exactly the same for all integer primitive types, changing all declarations in above method to long makes it work for long.

For smaller types, due to the implicit conversion to int (see the JLS for bitwise operations for details), instead of checking < 0, the check needs to mask the sign bit explicitly (0x8000 for short operands, 0x80 for byte operands, adjust casts and parameter declaration appropiately):

/**
 * Subtract two short's with overflow detection (r = d - s)
 */
public static short sub(final short d, final short s) throws ArithmeticException {
    int r = d - s;
    if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
        throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
    return (short) r;
}

(Note that above example uses the expression need for subtract overflow detection)


So how/why do these boolean expressions work? First, some logical thinking reveals that an overflow can only occur if the signs of both arguments are the same. Because, if one argument is negative and one positive, the result (of add) must be closer to zero, or in the extreme case one argument is zero, the same as the other argument. Since the arguments by themselves can't create an overflow condition, their sum can't create an overflow either.

So what happens if both arguments have the same sign? Lets take a look at the case both are positive: adding two arguments that create a sum larger than the types MAX_VALUE, will always yield a negative value, so an overflow occurs if arg1 + arg2 > MAX_VALUE. Now the maximum value that could result would be MAX_VALUE + MAX_VALUE (the extreme case both arguments are MAX_VALUE). For a byte (example) that would mean 127 + 127 = 254. Looking at the bit representations of all values that can result from adding two positive values, one finds that those that overflow (128 to 254) all have bit 7 set, while all that do not overflow (0 to 127) have bit 7 (topmost, sign) cleared. Thats exactly what the first (right) part of the expression checks:

if (((s & d & ~r) | (~s & ~d & r)) < 0)

(~s & ~d & r) becomes true, only if, both operands (s, d) are positive and the result (r) is negative (the expression works on all 32 bits, but the only bit we're interested in is the topmost (sign) bit, which is checked against by the < 0).

Now if both arguments are negative, their sum can never be closer to zero than any of the arguments, the sum must be closer to minus infinity. The most extreme value we can produce is MIN_VALUE + MIN_VALUE, which (again for byte example) shows that for any in range value (-1 to -128) the sign bit is set, while any possible overflowing value (-129 to -256) has the sign bit cleared. So the sign of the result again reveals the overflow condition. Thats what the left half (s & d & ~r) checks for the case where both arguments (s, d) are negative and a result that is positive. The logic is largely equivalent to the positive case; all bit patterns that can result from adding two negative values will have the sign bit cleared if and only if an underflow occured.

Smile简单爱 2024-09-11 19:17:40

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

从 Java 8 开始,java.lang.Math 提供 addExact, subtractExact , <代码>multiplyExactincrementExactdecrementExactnegateExact 用于执行指定操作的 int 和 long 参数的静态方法,在溢出时抛出 ArithmeticException。 (没有divideExact 方法——您必须自己检查一种特殊情况(MIN_VALUE / -1)。)

从Java 8 开始,java.lang.Math 还提供了toIntExact 到将 long 转换为 int,如果 long 的值不适合 int,则抛出 ArithmeticException。这对于例如使用未经检查的长数学计算整数之和,然后使用 toIntExact 在最后转换为 int 很有用(但要小心不要让总和溢出)。

如果您仍在使用旧版本的 Java,Google Guava 提供了IntMath 和 LongMath< /a> 用于检查加法、减法、乘法和求幂的静态方法(溢出时抛出)。这些类还提供计算阶乘和二项式系数的方法,这些方法在溢出时返回 MAX_VALUE(这不太方便检查)。 Guava 的原始实用程序类 SignedBytesUnsignedBytesShortsInts 提供 checkedCast用于缩小较大类型的方法(在下溢/溢出时抛出 IllegalArgumentException,不是 ArithmeticException),以及返回 MIN_VALUEsaturatingCast 方法溢出时为 MAX_VALUE

By default, Java's int and long math silently wrap around on overflow and underflow. (Integer operations on other integer types are performed by first promoting the operands to int or long, per JLS 4.2.2.)

As of Java 8, java.lang.Math provides addExact, subtractExact, multiplyExact, incrementExact, decrementExact and negateExact static methods for both int and long arguments that perform the named operation, throwing ArithmeticException on overflow. (There's no divideExact method -- you'll have to check the one special case (MIN_VALUE / -1) yourself.)

As of Java 8, java.lang.Math also provides toIntExact to cast a long to an int, throwing ArithmeticException if the long's value does not fit in an int. This can be useful for e.g. computing the sum of ints using unchecked long math, then using toIntExact to cast to int at the end (but be careful not to let your sum overflow).

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.

凶凌 2024-09-11 19:17:40

Java 不会对 int 或 long 基元类型的整数溢出执行任何操作,并且会忽略正整数和负整数的溢出。

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

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

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

有时负溢出被错误地称为下溢。下溢是指当值比表示允许的值更接近零时发生的情况。下溢发生在整数运算中,并且是预料之中的。当整数计算介于 -1 和 0 或 0 和 1 之间时,就会发生整数下溢。小数结果会截断为 0。这是整数算术的正常现象,是正常现象,不会被视为错误。但是,它可能会导致代码抛出异常。一个示例是,如果整数下溢的结果用作表达式中的除数,则会出现“ArithmeticException:/ by 0”异常。

考虑以下代码:

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

这会导致 x 被赋值为 0,并且随后对 bigValue / x 求值会引发异常“ArithmeticException:/被零”(即除以零),而不是 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,该 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 整数值的存储方式和行为符合 2 补码有符号整数表示形式。当结果值大于或小于最大或最小整数值时,将生成 2 的补码整数值。在没有明确设计为使用 2 补码行为的情况下(这是最普通的整数算术情况),生成的 2 补码值将导致编程逻辑或计算错误,如上例所示。一篇优秀的维基百科文章在这里描述了 2 补码二进制整数:二进制补码 - 维基百科

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

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

向上转换包括使用更大的基元类型来执行算术运算或表达式,然后确定结果值是否超出整数的最大值或最小值。即使使用向上转换,操作或表达式中的值或某些中间值仍然可能超出向上转换类型的最大值或最小值并导致溢出,这也不会被检测到,并且会导致意外和不期望的结果。通过分析或前置条件,当不进行向上转换的预防不可能或不可行时,可以通过向上转换来防止溢出。如果所讨论的整数已经是 long 基本类型,则 Java 中的基本类型不可能进行向上转换。

BigInteger 技术包括使用 BigInteger 进行算术运算或使用使用 BigInteger 的库方法进行表达式。 BigInteger 不会溢出。如有必要,它将使用所有可用内存。它的算术方法通常只比整数运算效率稍低。使用 BigInteger 的结果仍然有可能超出整数的最大值或最小值,但是,导致结果的算术不会发生溢出。编程和设计仍然需要确定如果 BigInteger 结果超出所需原始结果类型(例如 int 或 long)的最大值或最小值时该怎么办。

卡内基梅隆软件工程学院的 CERT 计划和 Oracle 创建了一套安全 Java 编程标准。标准中包括防止和检测整数溢出的技术。该标准在此处作为可免费访问的在线资源发布:CERT Oracle Java 安全编码标准

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

还提供了《CERT Oracle Java 安全编码标准》的书本形式和 PDF 形式。

Java doesn't do anything with integer overflow for either int or long primitive types and ignores overflow with positive and negative integers.

This answer first describes the of integer overflow, gives an example of how it can happen, even with intermediate values in expression evaluation, and then gives links to resources that give detailed techniques for preventing and detecting integer overflow.

Integer arithmetic and expressions reslulting in unexpected or undetected overflow are a common programming error. Unexpected or undetected integer overflow is also a well-known exploitable security issue, especially as it affects array, stack and list objects.

Overflow can occur in either a positive or negative direction where the positive or negative value would be beyond the maximum or minimum values for the primitive type in question. Overflow can occur in an intermediate value during expression or operation evaluation and affect the outcome of an expression or operation where the final value would be expected to be within range.

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.

Consider the following code:

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

which results in x being assigned 0 and the subsequent evaluation of bigValue / x throws an exception, "ArithmeticException: / by zero" (i.e. divide by zero), instead of y being assigned the value 2.

The expected result for x would be 858,993,458 which is less than the maximum int value of 2,147,483,647. However, the intermediate result from evaluating Integer.MAX_Value * 2, would be 4,294,967,294, which exceeds the maximum int value and is -2 in accordance with 2s complement integer representations. The subsequent evaluation of -2 / 5 evaluates to 0 which gets assigned to x.

Rearranging the expression for computing x to an expression that, when evaluated, divides before multiplying, the following code:

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

results in x being assigned 858,993,458 and y being assigned 2, which is expected.

The intermediate result from bigValue / 5 is 429,496,729 which does not exceed the maximum value for an int. Subsequent evaluation of 429,496,729 * 2 doesn't exceed the maximum value for an int and the expected result gets assigned to x. The evaluation for y then does not divide by zero. The evaluations for x and y work as expected.

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

There are techniques for avoiding unintentional integer overflow. Techinques may be categorized as using pre-condition testing, upcasting and BigInteger.

Pre-condition testing comprises examining the values going into an arithmetic operation or expression to ensure that an overflow won't occur with those values. Programming and design will need to create testing that ensures input values won't cause overflow and then determine what to do if input values occur that will cause overflow.

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.

The Carnegie Mellon Software Engineering Institute's CERT program and Oracle have created a set of standards for secure Java programming. Included in the standards are techniques for preventing and detecting integer overflow. The standard is published as a freely accessible online resource here: The CERT Oracle Secure Coding Standard for Java

The standard's section that describes and contains practical examples of coding techniques for preventing or detecting integer overflow is here: NUM00-J. Detect or prevent integer overflow

Book form and PDF form of The CERT Oracle Secure Coding Standard for Java are also available.

溇涏 2024-09-11 19:17:40

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

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

如果错误或可以简化,请随时纠正。我已经用乘法方法做了一些测试,主要是边缘情况,但它仍然可能是错误的。

Having just kinda run into this problem myself, here's my solution (for both multiplication and addition):

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

feel free to correct if wrong or if can be simplified. I've done some testing with the multiplication method, mostly edge cases, but it could still be wrong.

我爱人 2024-09-11 19:17:40

有些库提供安全算术运算,用于检查整数上溢/下溢。例如,Guava 的 IntMath.checkedAdd(int a, int b) 返回 ab 的和,前提是它不溢出,并且如果 a + b 在有符号 int 算术中溢出,则抛出 ArithmeticException

There are libraries that provide safe arithmetic operations, which check integer overflow/underflow . For example, Guava's IntMath.checkedAdd(int a, int b) returns the sum of a and b, provided it does not overflow, and throws ArithmeticException if a + b overflows in signed int arithmetic.

小伙你站住 2024-09-11 19:17:40

它环绕着。

例如:

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

prints

-2147483648
2147483647

自 java8 起,java.lang.Math 包具有诸如 addExact()multiplyExact() 之类的方法,它们会抛出 发生溢出时出现 ArithmeticException

It wraps around.

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

prints

-2147483648
2147483647

Since java8 the java.lang.Math package has methods like addExact() and multiplyExact() which will throw an ArithmeticException when an overflow occurs.

悟红尘 2024-09-11 19:17:40

我认为你应该使用这样的东西,它被称为向上转型:

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

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

这是相当可靠的来源。

I think you should use something like this and it is called 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;
}

You can read further here:
Detect or prevent integer overflow

It is quite reliable source.

终弃我 2024-09-11 19:17:40

它什么也不做——下溢/溢出只是发生。

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

但是,如果重要的话,您可以巧妙地进行计算以避免溢出,或者至少知道它何时会发生。你的情况如何?

It doesn't do anything -- the under/overflow just happens.

A "-1" that is the result of a computation that overflowed is no different from the "-1" that resulted from any other information. So you can't tell via some status or by inspecting just a value whether it's overflowed.

But you can be smart about your computations in order to avoid overflow, if it matters, or at least know when it will happen. What's your situation?

终止放荡 2024-09-11 19:17:40
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);
}
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);
}
往日情怀 2024-09-11 19:17:40

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

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

}
System.out.println(res);

将产生:

0

这种情况在这里讨论过:
整数溢出产生零.

There is one case, that is not mentioned above:

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

}
System.out.println(res);

will produce:

0

This case was discussed here:
Integer overflow produces Zero.

苦笑流年记忆 2024-09-11 19:17:40

我想这应该没问题。

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

I think this should be fine.

static boolean addWillOverFlow(int a, int b) {
    return (Integer.signum(a) == Integer.signum(b)) && 
            (Integer.signum(a) != Integer.signum(a+b)); 
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文