Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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. These methods throw an exception when an integer overflow would otherwise occur; any other conforming error handling is also acceptable.

Code Block
bgColor#ccccff

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

...

Either operation in this noncompliant code example could result in an overflow. When overflow occurs, the result will be incorrect.

Code Block
bgColor#FFcccc

public static int multAccum(int oldAcc, int newVal, int scale) {
  // May result in overflow
  return oldAcc + (newVal * scale);
}

...

This compliant solution uses the safeAdd() and safeMultiply() methods defined in the Precondition Testing section to perform secure integral operations or throw ArithmeticException on overflow.

Code Block
bgColor#ccccff

public static int multAccum(int oldAcc, int newVal, int scale)
                  throws ArithmeticException {
  return safeAdd(oldAcc, safeMultiply(newVal, scale));
}

...

This compliant solution shows the implementation of a method for checking whether a value of type long 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
bgColor#ccccff

public static long intRangeCheck(long value)
                   throws ArithmeticException {
  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
}

...

This compliant solution uses the BigInteger technique to detect overflow.

Code Block
bgColor#ccccff

private static final BigInteger bigMaxInt = 
  BigInteger.valueOf(Integer.MAX_VALUE);
private static final BigInteger bigMinInt =    
  BigInteger.valueOf(Integer.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
}

...

This noncompliant code example uses an AtomicInteger, which is part of the concurrency utilities. The concurrency utilities lack integer overflow checks.

Code Block
bgColor#FFcccc

class InventoryManager {
  private final AtomicInteger itemsInInventory = new AtomicInteger(100);

  //...
  public final void nextItem() {
    itemsInInventory.getAndIncrement();
  }
}

...

  • The number and order of accesses to itemsInInventory remain unchanged from the noncompliant code example.
  • All operations on the value of itemsInInventory are performed on a temporary local copy of its value.
  • The overflow check in this example is performed in inline code rather than encapsulated in a method call. This is an acceptable alternative implementation. The choice of method call versus inline code should be made according to your organization's standards and needs.
Code Block
bgColor#ccccff

class InventoryManager {
  private final AtomicInteger itemsInInventory =
      new AtomicInteger(100);

  public final void nextItem() {
    while (true) {
      int old = itemsInInventory.get();
      if (old == Integer.MAX_VALUE) {
        throw new ArithmeticException("Integer overflow");
      }
      int next = old + 1; // Increment
      if (itemsInInventory.compareAndSet(old, next)) {
        break;
      }
    } // end while
  } // end nextItem()
}

...

Android Implementation Details

Mezzofanti for Android contained an integer overflow which prevented the use of a big SD card. Mezzofanti contained an expression:

(int) StatFs.getAvailableBlocks() * (int) StatFs.getBlockSize() 

to calculate the available memory in an SD card, which could result in a negative value when the available memory is bigger than Integer.MAX_VALUE.

Note these methods are deprecated in API level 18 and replaced by getAvailableBlocksLong() and getBlockSizeLong().

Bibliography

 

03. Numeric Types and Operations (NUM)      03. Numeric Types and Operations (NUM)