Created
February 22, 2012 17:59
-
-
Save ctrueden/1886325 to your computer and use it in GitHub Desktop.
Benchmarks performance of several methods of detecting integer multiplication overflows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Overflows.java | |
// | |
import java.math.BigInteger; | |
/** | |
* Benchmarks performance of several methods of detecting integer overflows. | |
* | |
* @author Curtis Rueden | |
*/ | |
public final class Overflows { | |
// -- Main method -- | |
public static void main(final String[] args) { | |
// int[] iv = { Integer.MAX_VALUE / 2, 2 }; | |
// int[] iv = { 46340, 46341 }; | |
// int[] iv = { 46341, 46341 }; | |
// int[] iv = { 46340, 46342 }; | |
// int[] iv = { 65536, 32767 }; | |
// int[] iv = { 65535, 32768 }; | |
// int[] iv = { 65536, 32768 }; | |
// long[] lv = { Long.MAX_VALUE / 6, 2, 3 }; | |
final int num = 5; | |
final int[] iv = new int[num]; | |
final long[] lv = new long[num]; | |
for (int i = 0; i < num; i++) { | |
iv[i] = (int) (100 * Math.random()); | |
lv[i] = (long) (1000 * Math.random()); | |
} | |
final int iter = 1000000; | |
long start, end; | |
start = System.currentTimeMillis(); | |
for (int q = 0; q < iter; q++) | |
safeMultiply(iv); | |
end = System.currentTimeMillis(); | |
System.out.println("int (longs): " + (end - start) + " ms"); | |
start = System.currentTimeMillis(); | |
for (int q = 0; q < iter; q++) | |
safeMultiplyIntBits(iv); | |
end = System.currentTimeMillis(); | |
System.out.println("int (bits): " + (end - start) + " ms"); | |
start = System.currentTimeMillis(); | |
for (int q = 0; q < iter; q++) | |
safeMultiplyBigInteger(lv); | |
end = System.currentTimeMillis(); | |
System.out.println("long (BigIntegers): " + (end - start) + " ms"); | |
start = System.currentTimeMillis(); | |
for (int q = 0; q < iter; q++) | |
safeMultiplyLongBits(lv); | |
end = System.currentTimeMillis(); | |
System.out.println("long (bits): " + (end - start) + " ms"); | |
} | |
// -- Utility methods -- | |
/** | |
* Checks that the product of the given sizes does not exceed the 32-bit | |
* integer limit (i.e., {@link Integer#MAX_VALUE}). | |
* | |
* @param sizes list of sizes from which to compute the product | |
* @return the product of the given sizes | |
* @throws IllegalArgumentException if the total size exceeds 2GiB, which is | |
* the maximum size of an int in Java; or if any size argument is | |
* zero or negative | |
*/ | |
public static int safeMultiply(final int... sizes) | |
throws IllegalArgumentException | |
{ | |
if (sizes.length == 0) return 0; | |
long total = 1; | |
for (final int size : sizes) { | |
if (size < 1) { | |
throw new IllegalArgumentException("Invalid array size: " + | |
sizeAsProduct(sizes)); | |
} | |
total *= size; | |
if (total > Integer.MAX_VALUE) { | |
throw new IllegalArgumentException("Array size too large: " + | |
sizeAsProduct(sizes)); | |
} | |
} | |
// NB: The cast to int is safe here, due to the checks above. | |
return (int) total; | |
} | |
/** | |
* Checks that the product of the given sizes does not exceed the 32-bit | |
* integer limit (i.e., {@link Integer#MAX_VALUE}). | |
* | |
* @param sizes list of sizes from which to compute the product | |
* @return the product of the given sizes | |
* @throws IllegalArgumentException if the total size exceeds 2GiB, which is | |
* the maximum size of an int in Java; or if any size argument is | |
* zero or negative | |
*/ | |
public static int safeMultiplyIntBits(final int... sizes) | |
throws IllegalArgumentException | |
{ | |
if (sizes.length == 0) return 0; | |
int total = 1; | |
for (final int size : sizes) { | |
if (size < 1) { | |
throw new IllegalArgumentException("Invalid array size: " + | |
sizeAsProduct(sizes)); | |
} | |
if (willOverflow(total, size)) { | |
throw new IllegalArgumentException("Array size too large: " + | |
sizeAsProduct(sizes)); | |
} | |
total *= size; | |
} | |
return total; | |
} | |
private static final BigInteger LONG_MAX_VALUE = | |
new BigInteger("" + Long.MAX_VALUE); | |
/** | |
* Checks that the product of the given sizes does not exceed the 64-bit | |
* integer limit (i.e., {@link Long#MAX_VALUE}). | |
* | |
* @param sizes list of sizes from which to compute the product | |
* @return the product of the given sizes | |
* @throws IllegalArgumentException if the total size exceeds 8EiB, which is | |
* the maximum size of a long in Java; or if any size argument is | |
* zero or negative | |
*/ | |
public static long safeMultiplyBigInteger(final long... sizes) | |
throws IllegalArgumentException | |
{ | |
if (sizes.length == 0) return 0; | |
BigInteger total = BigInteger.ONE; | |
for (final long size : sizes) { | |
if (size < 1) { | |
throw new IllegalArgumentException("Invalid array size: " + | |
sizeAsProduct(sizes)); | |
} | |
total = total.multiply(new BigInteger("" + size)); | |
if (total.compareTo(LONG_MAX_VALUE) > 0) { | |
throw new IllegalArgumentException("Array size too large: " + | |
sizeAsProduct(sizes)); | |
} | |
} | |
// NB: The downcast to long is safe here, due to the checks above. | |
return total.longValue(); | |
} | |
/** | |
* Checks that the product of the given sizes does not exceed the 64-bit | |
* integer limit (i.e., {@link Long#MAX_VALUE}). | |
* | |
* @param sizes list of sizes from which to compute the product | |
* @return the product of the given sizes | |
* @throws IllegalArgumentException if the total size exceeds 8EiB, which is | |
* the maximum size of a long in Java; or if any size argument is | |
* zero or negative | |
*/ | |
public static long safeMultiplyLongBits(final long... sizes) | |
throws IllegalArgumentException | |
{ | |
if (sizes.length == 0) return 0; | |
long total = 1; | |
for (final long size : sizes) { | |
if (size < 1) { | |
throw new IllegalArgumentException("Invalid array size: " + | |
sizeAsProduct(sizes)); | |
} | |
if (willOverflow(total, size)) { | |
throw new IllegalArgumentException("Array size too large: " + | |
sizeAsProduct(sizes)); | |
} | |
total *= size; | |
} | |
return total; | |
} | |
// -- Helper methods -- | |
private static String sizeAsProduct(final int... sizes) { | |
final StringBuilder sb = new StringBuilder(); | |
boolean first = true; | |
for (final int size : sizes) { | |
if (first) first = false; | |
else sb.append(" x "); | |
sb.append(size); | |
} | |
return sb.toString(); | |
} | |
private static String sizeAsProduct(final long... sizes) { | |
final StringBuilder sb = new StringBuilder(); | |
boolean first = true; | |
for (final long size : sizes) { | |
if (first) first = false; | |
else sb.append(" x "); | |
sb.append(size); | |
} | |
return sb.toString(); | |
} | |
private static boolean willOverflow(final int v1, final int v2) { | |
return Integer.MAX_VALUE / v1 < v2; | |
} | |
private static boolean willOverflow(final long v1, final long v2) { | |
return Long.MAX_VALUE / v1 < v2; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment