...
- Upcasting. 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; larger expressions without per-operation bounds checking may overflow the larger type. 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.
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.TypeBigInteger
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 and throw anArithmeticException
if the final result is outside the range of the original smaller type.
The The pre-condition 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 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 techniquesOperations on objects of type BigInteger
can also be significantly less efficient than operations on the original primitive integer type.
Pre-condition Testing
The following code example shows the necessary pre-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.
...
This compliant solution uses the preAdd()
and preMultiply()
methods defined above to indicate errors if the corresponding operations might in the Pre-condition testing section to perform secure integral operations or throw ArithmeticException
on 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 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 BigInteger
technique when the original variables are of type long
.
This compliant solution shows the implementation of a method for checking whether a long
value falls within the representable range of an int
. The implementations of range checks for the smaller primitive integer types are similar.
This compliant solution shows the implementation of a method for checking whether a long
value falls within the representable range of an int
using the upcasting technique. The implementations of range checks for the smaller primitive integer types are similar.
Code Block | ||
---|---|---|
| ||
public static long intRangeCheck(long value) throws ArithmeticOverflow {
if ((value < Integer.MIN | ||
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
)
-cast
}
|
This approach cannot be applied for type long
because long
is the largest primitive integral type. Use the BigInteger
technique when the original variables are of type long
.
Compliant Solution (BigInteger
)
This compliant solution uses the BigInteger
technique to detect overflowType 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.
Code Block | ||
---|---|---|
| ||
private static final BigInteger bigMaxInt = BigInteger.valueOf(Int.MAX_VALUE); private static final BigInteger bigMinInt = BigInteger.valueOf(Int.MIN_VALUE); public static BigInteger intRangeCheck(BigInteger val) throws ArithmeticException { if (val.compareTo(bigMaxInt) == 1 || val.compareTo(bigMinInt) == -1) { throw new ArithmeticException("Integer overflow"); } return val; } public static int multAccum(int oldAcc, int newVal, int scale) throws ArithmeticException { BigInteger product = BigInteger.valueOf(newVal).multiply(BigInteger.valueOf(scale)); BigInteger res = intRangeCheck(BigInteger.valueOf(oldAcc).add(product)); return res.intValue(); // safe conversion } |
...