...
Code Block | ||
---|---|---|
| ||
public int multAccum(int oldAcc, int newVal, int scale) { // May result in overflow return oldAcc + (newVal * scale); } |
Compliant Solution (Pre-condition the
...
inputs)
The code example below shows the necessary pre-conditioning checks required for each arithmetic operation on arguments of type int
. The checks for the other integral types are analogous.
...
Note that although these pre-conditioning checks are correct, more efficient code may well be possible. Further, the checks can be simplified when the original type was char
. Because the range of type char
includes only positive values, all comparisons with negative values may be omitted.
Compliant Solution (Pre-condition the inputs)
Code Block | ||
---|---|---|
| ||
public int multAccum(int oldAcc, int newVal, int scale) throws ArithmeticException { preMultiply(newVal, Scale); final int temp = newVal * scale; preAdd(oldAcc, temp); return oldAcc + temp; } |
Compliant Solution (Use a Larger Type and Downcast)
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
. Therefore, we can perform an operation using the larger type and range-check before down casting to the original type. Note, however, that this guarantee holds only for a one arithmetic operation; larger expressions without per-operation bounds checks 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
.
...
This compliant solution shows the implementation of a method for checking whether a long value falls within the representable range of they int
. The implementations of range checks for the smaller primitive integer types are exactly analogous.
Code Block | ||
---|---|---|
| ||
public long intRangeCheck(long value) throws ArithmeticOverflow { if ((value < Integer.MIN_VALUE) || (value > Integer.MAX_VALUE)) { throw new ArithmeticException("Integer overflow"); } return value; } public 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 (Use 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 in place of primitive arithmetic operators; this may obscure the intent of the code.
Note that operations on objects of type BigInteger
may be significantly less efficient than operations on the original primitive integer type. Whether this loss of efficiency is important will depend on the context in which the code is used.
...
Code Block | ||
---|---|---|
| ||
private static final BigInteger bigMaxInt = BigInteger.valueOf(Integer.MAX_VALUE); private static final BigInteger bigMinInt = BigInteger.valueOf(Integer.MIN_VALUE); public BigInteger intRangeCheck(BigInteger val) throws ArithmeticException { if (val.compareTo(bigMaxInt) == 1 || val.compareTo(bigMinInt) == -1) { throw new ArithmeticException("Integer overflow"); } return val; } public 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 } |
...