...
Wiki Markup |
---|
Failure to account for integer overflow has resulted in failures of real systems, for example, when implementing the {{compareTo()}} method. The meaning of the return value of the {{compareTo()}} method is defined only in terms of its sign and whether it is zero; the magnitude of the return value is irrelevant. Consequently, an apparent but incorrect optimization would be to subtract the operands and return the result. For operands of opposite signs, this can result in integer overflow, consequently violating the {{compareTo()}} contract \[[Bloch 2008, Item 12|AA. Bibliography#Bloch 08]\]. |
...
Comparison of Compliant Techniques
The three main techniques for detecting unintended integer overflow are
- Pre-condition testing of the inputs. Check the inputs to each arithmetic operator to ensure that overflow cannot occur. Throw an
ArithmeticException
when the operation would overflow if it were performed; otherwise, perform the operation. This technique is referred to as "Pre-condition the inputs" in the remainder of this guideline..
- UpcastingUse a larger type and downcast. Cast the inputs to the next larger primitive integer type and perform the arithmetic in the larger size. Check each intermediate result for overflow of the original smaller type, and throw an
ArithmeticException
if the range check fails. Note that the range check must be performed after each arithmetic operation. Downcast the final result to the original smaller type before assigning to the result variable. This approach cannot be used for typelong
becauselong
is already the largest primitive integer type.
- Use
BigInteger
. Convert the inputs into objects of typeBigInteger
and perform all arithmetic usingBigInteger
methods. Throw anArithmeticException
if the final result is outside the range of the original smaller type; otherwise, convert back to the intended result type.
The "Prepre-condition the inputs" testing technique requires different pre-condition tests for each arithmetic operation. This can be somewhat more difficult to understand than either of the other two approaches.
The "Use a larger type and downcast" upcast technique is the preferred approach for the cases to which it applies. The checks it requires are simpler than those of the previous technique; it is substantially more efficient than using BigInteger
.
The "Use BigInteger
" technique is conceptually the simplest of the three techniques. However, it requires the use of method calls for each operation in place of primitive arithmetic operators; this may obscure the intended meaning of the code. This technique consumes more time and memory than the other techniques.
Pre-
...
condition Testing
The following code example shows the necessary pre-conditioning condition checks required for each arithmetic operation on arguments of type int
. The checks for the other integral types are analogous. In this example, we choose to throw an exception when integer overflow would occur; any other error handling is also acceptable.
Code Block | ||
---|---|---|
| ||
static final int preAdd(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 preSubtract(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 preMultiply(int left, int right) throws ArithmeticException { if (right>0right > 0 ? left > Integer.MAX_VALUE/right || left < Integer.MIN_VALUE/right : (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 preDivide(int left, int right) throws ArithmeticException { if ((left == Integer.MIN_VALUE) && (right == -1)) { throw new ArithmeticException("Integer overflow"); } return left / right; } static final int preAbs(int a) throws ArithmeticException { if (a == Integer.MIN_VALUE) { throw new ArithmeticException("Integer overflow"); } return Math.abs(a); } static final int preNegate(int a) throws ArithmeticException { if (a == Integer.MIN_VALUE) { throw new ArithmeticException("Integer overflow"); } return -a; } |
These method calls are likely to be inlined by most JITs.
These checks can be simplified when the original type is char
. Because the range of type char
includes only positive values, all comparisons with negative values may be omitted.
...
Code Block | ||
---|---|---|
| ||
public static int multAccum(int oldAcc, int newVal, int scale) { // May result in overflow return oldAcc + (newVal * scale); } |
Compliant Solution (Pre-Condition
...
Testing)
This compliant solution uses the preAdd()
and preMultiply()
methods defined above to indicate errors if the corresponding operations might overflow.
Code Block | ||
---|---|---|
| ||
public static int multAccum(int oldAcc, int newVal, int scale) throws ArithmeticException { return preAdd(oldAcc, preMultiply(newVal, scale)); } |
Compliant Solution (
...
Upcasting)
For all integral types other than long
, the next larger integral type can represent the result of any single integral operation. For example, operations on values of type int
can be safely performed using type long
. As a result, we can perform an operation using the larger type and range-check before down casting downcasting to the original type. Note, however, that this guarantee holds only for a single arithmetic operation; larger expressions without per-operation bounds checking may overflow the larger type.
This approach cannot be applied for type long
because long
is the largest primitive integral type. Use the "Use BigInteger
" technique when the original variables are of type long
.
...
Code Block | ||
---|---|---|
| ||
public static long intRangeCheck(long value) throws ArithmeticOverflow { if ((value < Integer.MIN_VALUE) || (value > Integer.MAX_VALUE)) { throw new ArithmeticException("Integer overflow"); } return value; } public static int multAccum(int oldAcc, int newVal, int scale) throws ArithmeticException { final long res = intRangeCheck(((long) oldAcc) + intRangeCheck((long) newVal * (long) scale)); return (int) res; // safe down-cast } |
Compliant Solution (
...
BigInteger
)
Type BigInteger
is the standard arbitrary-precision integer type provided by the Java standard libraries. The arithmetic operations implemented as methods of this type cannot themselves overflow; instead, they produce the numerically correct result. As a consequence, compliant code performs only a single range checkâ”just before converting the final result to the original smaller type. This property provides conceptual simplicity. An unfortunate consequence of this technique is that compliant code must be written using method calls instead of primitive arithmetic operators; this can obscure the intent of the code. Operations on objects of type BigInteger
can also be significantly less efficient than operations on the original primitive integer type.
...