...
See the following example:
Noncompliant Code Example
In we have the following simple method the result could overflow
...
yes |
| *= | yes |
| >> | no |
| > | no | ||
yes |
| /= | yes |
| & | no |
| >= | no | ||
/ | yes |
| %= | yes |
| | | no |
| <= | no | |
% | yes |
| <<= | yes |
| ^ | no |
| == | no | |
++ | yes |
| >>= | no |
| ~ | no |
| != | no | |
-- | yes |
| &= | no |
| ! | no |
| && | no | |
= | no |
| |= | no |
| un+ | no |
| || | no | |
+= | yes |
| ^= | no |
| un- | yes |
| ?: | no |
|
Addition
Addition (and all operations) in Java are performed in signed numbers as Java does not support unsigned numbers
Noncompliant Code Example
In this example the addition could result in overflow
Code Block | ||
---|---|---|
| ||
public int do_operation(int a,int b) { int temp = a + b; //Could result in overflow //do other processing return temp; } |
Compliant Solution (Bounds Checking)
A solution would be to explicitly check the range of each arithmetic operation and throw an ArithmeticException on overflow, otherwise downcast the value to an integer. For arithmetical operations on really big numbers one should always use the BigInteger Class
...
By using the BigInteger class there is no chance to overflow (see section on BigInteger class) but the performance is degraded so it should be used only on really large operations
Subtraction
Care must be taken in subtraction operations as well as these can overflow as well.
Noncompliant Code Example
Code Block | ||
---|---|---|
| ||
public int do_operation(int a,int b) { int temp = a - b; //Could result in overflow //perform other processing return temp; } |
Compliant Code Example
The appropriate way is to check explicitely the range before doing the subtraction
...
Code Block | ||
---|---|---|
| ||
public bool underflow(int a, int b) { java.math.BigInteger ba = new java.math.BigInteger(String.valueOf(a)); java.math.BigInteger bb = new java.math.BigInteger(String.valueOf(b)); java.math.BigInteger br = ba.subtract(bb); if(br.compareTo(java.math.BigInteger.valueOf(Integer.MAX_VALUE)) == 1 || br.compareTo(java.math.BigInteger.valueOf(Integer.MIN_VALUE))== -1) return true;//We have underflow //Can proceed return false } public int do_operation(int a, int b) throws ArithmeticException { if(undeflow(a,b)) throw ArithmeticException; else //we are within range safely perform the addition } |
Multiplication
This noncompliant code example, can result in a signed integer overflow during the multiplication of the signed operands a and b. If this behaviour is unanticipated, the resulting value may lead to undefined behaviour
Noncompliant Code Example
Code Block | ||
---|---|---|
| ||
int a,b,result //do stuff result = a*b;//May result in overflow |
Compliant Code Example
Since in this platform the size of type long (64 bits) is twice the size of type int (32 bits) we should perform the multiplication in terms of long and if the product is in the integer range we downcast the result to int
Code Block | ||
---|---|---|
| ||
int a,b,result; long temp = (long) a\* (long)b; if(temp > Integer.MAX_VALUE || temp < Integer.MIN_VALUE) throw ArithmeticException;//overflow else result = (int) temp;//Value within range, safe to downcast |
Division
Although Java throws a java.lang.ArithmeticException: / by zero exception for division by zero, there is the same issue as in C\C++ when dividing the Integer.MIN_VALUE with -1. It produces Integer.MIN_VALUE unexpectedly
...
A non-compliant example is:
Noncompliant Code Example
Code Block | ||
---|---|---|
| ||
int a,b,result result = a/b; |
Compliant Code Example
Code Block | ||
---|---|---|
| ||
if(a == Integer.MIN_VALUE && b == -1) throw ArithmeticException;//May be Integer.MIN_VALUE again???? else result = a/b;//safe operation |
Division
For modulo operator the only problem is if we take the modulo of Integer.MIN_VALUE with -1. The result is always 0 in JAVA so we are back to the previous rule (for division)
Unary Negation
If we negate Integer.MIN_VALUE we get Integer.MIN_VALUE. So we explicitely check the range
Code Block | ||
---|---|---|
| ||
if(a == Integer.MIN_VALUE) throw ArithmeticException; else result = --a; |
SHIFTING
The shift in java is quite different than in C\C++.
...
Code Block | ||
---|---|---|
| ||
if(shift_value > 31 or shift_value <0)if(shift_value > 31 or shift_value <0) throw ArithmeticException; else int val = 2 << shift_value; throw ArithmeticException; else int val = 2 << shift_value; |
Unsigned Right shifting >>>
It is identical to the right-shift operator if the shifted value is positive. If it is negative the sign value can
change because the left-operand high-order bit is not retained and the sign value can change; Excerpt
from JLS:
"if n is negative, the result is equal to that of the expression (n>>s)(2<<~s) if the type of the left-hand operand is int, and to the result of the expression (n>>s)(2L<<~s) if the type of the left-hand operand is long. The added term (2<<~s) or (2L<<~s) cancels out the propagated sign bit. (Note that, because of the implicit masking of the right-hand operand of a shift operator, ~s as a shift distance is equivalent to 31-s when shifting an int value and to 63-s when shifting a longvalue.)"
For example: -32 >>> 2 = (-32 >> 2 ) + ( 2 << ~2 ) = 1073741816
Operations Requiring Really Long Numbers
For these operations the BigInteger class should be used. According to SUN BigInteger Class:
...
For instance operations on long are operations on 64 bits. For example addition:
Compliant Code Example
Code Block | ||
---|---|---|
| ||
java.math.BigInteger big_long_max = new java.math.BigInteger(String.valueOf(Long.MAX_VALUE)); System.out.println("big_long="+big_long_max); big_long_max = big_long_max.add(java.math.BigInteger.valueOf(1));//same as ++big_long_max System.out.println("big_long="+big_long_max); These print big_long=9223372036854775807 big_long=9223372036854775808//exceeds the maximum range of long, no problem java.math.BigInteger big_long_min = new java.math.BigInteger(String.valueOf(Long.MIN_VALUE)); System.out.println("big_long_min="+big_long_min); big_long_min = big_long_min.subtract(java.math.BigInteger.valueOf(1));//same as --big_long_min System.out.println("big_long_min="+big_long_min);//goes bellow minimum range of long, no problem These print: big_long_min=-9223372036854775808 big_long_min=-9223372036854775809 if(big_long < Long.MAX_VALUE && big_long > Long.MIN_VALUE)//value within range can go to the primitive type long value = big_log.longValue();//get primitive type else //Perform error handling. We can not downcast since the value can not be represented as a long |
...