6708398: Support integer overflow
authorsherman
Thu, 16 Feb 2012 11:43:20 -0800
changeset 11905 646e7e50c2d7
parent 11904 f0eca4f34170
child 11906 dc984e35d8a6
6708398: Support integer overflow Summary: Added add/sub/multiply/toIntExact methods to j.l.Math and StrictMath classes Reviewed-by: emcmanus Contributed-by: roger.riggs@oracle.com
jdk/src/share/classes/java/lang/Math.java
jdk/src/share/classes/java/lang/StrictMath.java
jdk/test/java/lang/Math/ExactArithTests.java
jdk/test/java/lang/StrictMath/ExactArithTests.java
--- a/jdk/src/share/classes/java/lang/Math.java	Wed Feb 15 23:45:17 2012 -0800
+++ b/jdk/src/share/classes/java/lang/Math.java	Thu Feb 16 11:43:20 2012 -0800
@@ -81,6 +81,22 @@
  * floating-point approximation.  Not all approximations that have 1
  * ulp accuracy will automatically meet the monotonicity requirements.
  *
+ * <p>
+ * The platform uses signed two's complement integer arithmetic with
+ * int and long primitive types.  The developer should choose
+ * the primitive type to ensure that arithmetic operations consistently
+ * produce correct results, which in some cases means the operations
+ * will not overflow the range of values of the computation.
+ * The best practice is to choose the primitive type and algorithm to avoid
+ * overflow. In cases where the size is {@code int} or {@code long} and
+ * overflow errors need to be detected, the methods {@code addExact},
+ * {@code subtractExact}, {@code multiplyExact}, and {@code toIntExact}
+ * throw an {@code ArithmeticException} when the results overflow.
+ * For other arithmetic operations such as divide, absolute value,
+ * increment, decrement, and negation overflow occurs only with
+ * a specific minimum or maximum value and should be checked against
+ * the minimum or maximum as appropriate.
+ *
  * @author  unascribed
  * @author  Joseph D. Darcy
  * @since   JDK1.0
@@ -719,6 +735,137 @@
     }
 
     /**
+     * Returns the sum of its arguments,
+     * throwing an exception if the result overflows an {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
+     */
+    public static int addExact(int x, int y) {
+        int r = x + y;
+        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
+        if (((x ^ r) & (y ^ r)) < 0) {
+            throw new ArithmeticException("integer overflow");
+        }
+        return r;
+    }
+
+    /**
+     * Returns the sum of its arguments,
+     * throwing an exception if the result overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     */
+    public static long addExact(long x, long y) {
+        long r = x + y;
+        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
+        if (((x ^ r) & (y ^ r)) < 0) {
+            throw new ArithmeticException("long overflow");
+        }
+        return r;
+    }
+
+    /**
+     * Returns the difference of the arguments,
+     * throwing an exception if the result overflows an {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
+     */
+    public static int subtractExact(int x, int y) {
+        int r = x - y;
+        // HD 2-12 Overflow iff the arguments have different signs and
+        // the sign of the result is different than the sign of x
+        if (((x ^ y) & (x ^ r)) < 0) {
+            throw new ArithmeticException("integer overflow");
+        }
+        return r;
+    }
+
+    /**
+     * Returns the difference of the arguments,
+     * throwing an exception if the result overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     */
+    public static long subtractExact(long x, long y) {
+        long r = x - y;
+        // HD 2-12 Overflow iff the arguments have different signs and
+        // the sign of the result is different than the sign of x
+        if (((x ^ y) & (x ^ r)) < 0) {
+            throw new ArithmeticException("long overflow");
+        }
+        return r;
+    }
+
+    /**
+     * Returns the product of the arguments,
+     * throwing an exception if the result overflows an {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
+     */
+    public static int multiplyExact(int x, int y) {
+        long r = (long)x * (long)y;
+        if ((int)r != r) {
+            throw new ArithmeticException("long overflow");
+        }
+        return (int)r;
+    }
+
+    /**
+     * Returns the product of the arguments,
+     * throwing an exception if the result overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     */
+    public static long multiplyExact(long x, long y) {
+        long r = x * y;
+        long ax = Math.abs(x);
+        long ay = Math.abs(y);
+        if (((ax | ay) >>> 31 != 0)) {
+            // Some bits greater than 2^31 that might cause overflow
+            // Check the result using the divide operator
+            // and check for the special case of Long.MIN_VALUE * -1
+           if (((y != 0) && (r / y != x)) ||
+               (x == Long.MIN_VALUE && y == -1)) {
+                throw new ArithmeticException("long overflow");
+            }
+        }
+        return r;
+    }
+
+    /**
+     * Returns the value of the {@code long} argument;
+     * throwing an exception if the value overflows an {@code int}.
+     *
+     * @param value the long value
+     * @return the argument as an int
+     * @throws ArithmeticException if the {@code argument} overflows an int
+     */
+    public static int toIntExact(long value) {
+        if ((int)value != value) {
+            throw new ArithmeticException("integer overflow");
+        }
+        return (int)value;
+    }
+
+    /**
      * Returns the absolute value of an {@code int} value.
      * If the argument is not negative, the argument is returned.
      * If the argument is negative, the negation of the argument is returned.
@@ -1737,7 +1884,7 @@
     }
 
     /**
-     * Return {@code d} &times;
+     * Returns {@code d} &times;
      * 2<sup>{@code scaleFactor}</sup> rounded as if performed
      * by a single correctly rounded floating-point multiply to a
      * member of the double value set.  See the Java
@@ -1844,7 +1991,7 @@
     }
 
     /**
-     * Return {@code f} &times;
+     * Returns {@code f} &times;
      * 2<sup>{@code scaleFactor}</sup> rounded as if performed
      * by a single correctly rounded floating-point multiply to a
      * member of the float value set.  See the Java
--- a/jdk/src/share/classes/java/lang/StrictMath.java	Wed Feb 15 23:45:17 2012 -0800
+++ b/jdk/src/share/classes/java/lang/StrictMath.java	Thu Feb 16 11:43:20 2012 -0800
@@ -56,6 +56,22 @@
  * {@code sinh}, {@code cosh}, {@code tanh},
  * {@code hypot}, {@code expm1}, and {@code log1p}.
  *
+ * <p>
+ * The platform uses signed two's complement integer arithmetic with
+ * int and long primitive types.  The developer should choose
+ * the primitive type to ensure that arithmetic operations consistently
+ * produce correct results, which in some cases means the operations
+ * will not overflow the range of values of the computation.
+ * The best practice is to choose the primitive type and algorithm to avoid
+ * overflow. In cases where the size is {@code int} or {@code long} and
+ * overflow errors need to be detected, the methods {@code addExact},
+ * {@code subtractExact}, {@code multiplyExact}, and {@code toIntExact}
+ * throw an {@code ArithmeticException} when the results overflow.
+ * For other arithmetic operations such as divide, absolute value,
+ * increment, decrement, and negation overflow occurs only with
+ * a specific minimum or maximum value and should be checked against
+ * the minimum or maximum as appropriate.
+ *
  * @author  unascribed
  * @author  Joseph D. Darcy
  * @since   1.3
@@ -699,7 +715,111 @@
     }
 
     /**
-     * Returns the absolute value of an {@code int} value..
+     * Returns the sum of its arguments,
+     * throwing an exception if the result overflows an {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
+     * @see Math#addExact(int,int)
+     * @since 1.8
+     */
+    public static int addExact(int x, int y) {
+        return Math.addExact(x, y);
+    }
+
+    /**
+     * Returns the sum of its arguments,
+     * throwing an exception if the result overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     * @see Math#addExact(long,long)
+     * @since 1.8
+     */
+    public static long addExact(long x, long y) {
+        return Math.addExact(x, y);
+    }
+
+    /**
+     * Return the difference of the arguments,
+     * throwing an exception if the result overflows an {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
+     * @see Math#subtractExact(int,int)
+     * @since 1.8
+     */
+    public static int subtractExact(int x, int y) {
+        return Math.subtractExact(x, y);
+    }
+
+    /**
+     * Return the difference of the arguments,
+     * throwing an exception if the result overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     * @see Math#subtractExact(long,long)
+     * @since 1.8
+     */
+    public static long subtractExact(long x, long y) {
+        return Math.subtractExact(x, y);
+    }
+
+    /**
+     * Return the product of the arguments,
+     * throwing an exception if the result overflows an {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
+     * @see Math#multiplyExact(int,int)
+     * @since 1.8
+     */
+    public static int multiplyExact(int x, int y) {
+        return Math.multiplyExact(x, y);
+    }
+
+    /**
+     * Return the product of the arguments,
+     * throwing an exception if the result overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     * @see Math#multiplyExact(long,long)
+     * @since 1.8
+     */
+    public static long multiplyExact(long x, long y) {
+        return Math.multiplyExact(x, y);
+    }
+
+    /**
+     * Return the value of the {@code long} argument;
+     * throwing an exception if the value overflows an {@code int}.
+     *
+     * @param value the long value
+     * @return the argument as an int
+     * @throws ArithmeticException if the {@code argument} overflows an int
+     * @see Math#toIntExact(int)
+     * @since 1.8
+     */
+    public static int toIntExact(long value) {
+        return Math.toIntExact(value);
+    }
+
+    /**
+     * Returns the absolute value of an {@code int} value.
      * If the argument is not negative, the argument is returned.
      * If the argument is negative, the negation of the argument is returned.
      *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Math/ExactArithTests.java	Thu Feb 16 11:43:20 2012 -0800
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.math.BigInteger;
+
+/**
+ * @test Test for Math.*Exact integer and long methods.
+ * @bug 6708398
+ * @summary Basic tests for Math exact arithmetic operations.
+ *
+ * @author Roger Riggs
+ */
+public class ExactArithTests {
+
+    /**
+     * The count of test errors.
+     */
+    private static int errors = 0;
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        testIntegerExact();
+        testLongExact();
+
+        if (errors > 0) {
+            throw new RuntimeException(errors + " errors found in ExactArithTests.");
+        }
+    }
+
+    static void fail(String message) {
+        errors++;
+        System.err.println(message);
+    }
+
+    /**
+     * Test Math.addExact, multiplyExact, subtractExact, toIntValue methods
+     * with {@code int} arguments.
+     */
+    static void testIntegerExact() {
+        testIntegerExact(0, 0);
+        testIntegerExact(1, 1);
+        testIntegerExact(1, -1);
+        testIntegerExact(-1, 1);
+        testIntegerExact(1000, 2000);
+
+        testIntegerExact(Integer.MIN_VALUE, Integer.MIN_VALUE);
+        testIntegerExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testIntegerExact(Integer.MIN_VALUE, 1);
+        testIntegerExact(Integer.MAX_VALUE, 1);
+        testIntegerExact(Integer.MIN_VALUE, 2);
+        testIntegerExact(Integer.MAX_VALUE, 2);
+        testIntegerExact(Integer.MIN_VALUE, -1);
+        testIntegerExact(Integer.MAX_VALUE, -1);
+        testIntegerExact(Integer.MIN_VALUE, -2);
+        testIntegerExact(Integer.MAX_VALUE, -2);
+
+    }
+
+    /**
+     * Test exact arithmetic by comparing with the same operations using long
+     * and checking that the result is the same as the integer truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testIntegerExact(int x, int y) {
+        try {
+            // Test addExact
+            int sum = Math.addExact(x, y);
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 != sum2) {
+                fail("FAIL: int Math.addExact(" + x + " + " + y + ") = " + sum + "; expected Arithmetic exception");
+            } else if (sum != sum2) {
+                fail("FAIL: long Math.addExact(" + x + " + " + y + ") = " + sum + "; expected: " + sum2);
+            }
+        } catch (ArithmeticException ex) {
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 == sum2) {
+                fail("FAIL: int Math.addExact(" + x + " + " + y + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+
+        try {
+            // Test subtractExact
+            int diff = Math.subtractExact(x, y);
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 != diff2) {
+                fail("FAIL: int Math.subtractExact(" + x + " - " + y + ") = " + diff + "; expected: " + diff2);
+            }
+
+        } catch (ArithmeticException ex) {
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 == diff2) {
+                fail("FAIL: int Math.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            int product = Math.multiplyExact(x, y);
+            long m2 = (long) x * (long) y;
+            if ((int) m2 != m2) {
+                fail("FAIL: int Math.multiplyExact(" + x + " * " + y + ") = " + product + "; expected: " + m2);
+            }
+        } catch (ArithmeticException ex) {
+            long m2 = (long) x * (long) y;
+            if ((int) m2 == m2) {
+                fail("FAIL: int Math.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Test Math.addExact, multiplyExact, subtractExact, toIntExact methods
+     * with {@code long} arguments.
+     */
+    static void testLongExact() {
+        testLongExactTwice(0, 0);
+        testLongExactTwice(1, 1);
+        testLongExactTwice(1, -1);
+        testLongExactTwice(1000, 2000);
+
+        testLongExactTwice(Long.MIN_VALUE, Long.MIN_VALUE);
+        testLongExactTwice(Long.MAX_VALUE, Long.MAX_VALUE);
+        testLongExactTwice(Long.MIN_VALUE, 1);
+        testLongExactTwice(Long.MAX_VALUE, 1);
+        testLongExactTwice(Long.MIN_VALUE, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Long.MIN_VALUE, -1);
+        testLongExactTwice(Long.MAX_VALUE, -1);
+        testLongExactTwice(Long.MIN_VALUE, -2);
+        testLongExactTwice(Long.MAX_VALUE, -2);
+        testLongExactTwice(Long.MIN_VALUE/2, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE, -Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE+1, Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MAX_VALUE+1, -Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MIN_VALUE-1, Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE-1, -Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE/2, 2);
+
+    }
+
+    /**
+     * Test each of the exact operations with the arguments and
+     * with the arguments reversed.
+     * @param x
+     * @param y
+     */
+    static void testLongExactTwice(long x, long y) {
+        testLongExact(x, y);
+        testLongExact(y, x);
+    }
+
+
+    /**
+     * Test long exact arithmetic by comparing with the same operations using BigInteger
+     * and checking that the result is the same as the long truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testLongExact(long x, long y) {
+        BigInteger resultBig = null;
+        final BigInteger xBig = BigInteger.valueOf(x);
+        final BigInteger yBig = BigInteger.valueOf(y);
+        try {
+            // Test addExact
+            resultBig = xBig.add(yBig);
+            long sum = Math.addExact(x, y);
+            checkResult("long Math.addExact", x, y, sum, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.addExact(" + x + " + " + y + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test subtractExact
+            resultBig = xBig.subtract(yBig);
+            long diff = Math.subtractExact(x, y);
+            checkResult("long Math.subtractExact", x, y, diff, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            resultBig = xBig.multiply(yBig);
+            long product = Math.multiplyExact(x, y);
+            checkResult("long Math.multiplyExact", x, y, product, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test toIntExact
+            int value = Math.toIntExact(x);
+            if ((long)value != x) {
+                fail("FAIL: " + "long Math.toIntExact" + "(" + x + ") = " + value + "; expected an arithmetic exception: ");
+            }
+        } catch (ArithmeticException ex) {
+            if (resultBig.bitLength() <= 32) {
+                fail("FAIL: long Math.toIntExact(" + x + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Compare the expected and actual results.
+     * @param message message for the error
+     * @param x first argument
+     * @param y second argument
+     * @param result actual result value
+     * @param expected expected result value
+     */
+    static void checkResult(String message, long x, long y, long result, BigInteger expected) {
+        BigInteger resultBig = BigInteger.valueOf(result);
+        if (!inLongRange(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected an arithmetic exception: ");
+        } else if (!resultBig.equals(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected " + expected);
+        }
+    }
+
+    /**
+     * Check if the value fits in 64 bits (a long).
+     * @param value
+     * @return true if the value fits in 64 bits (including the sign).
+     */
+    static boolean inLongRange(BigInteger value) {
+        return value.bitLength() <= 63;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StrictMath/ExactArithTests.java	Thu Feb 16 11:43:20 2012 -0800
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.math.BigInteger;
+
+/**
+ * @test Test for StrictMath.*Exact integer and long methods.
+ * @bug 6708398
+ * @summary Basic tests for StrictMath exact arithmetic operations.
+ *
+ * @author Roger Riggs
+ */
+public class ExactArithTests {
+
+    /**
+     * The count of test errors.
+     */
+    private static int errors = 0;
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        testIntegerExact();
+        testLongExact();
+
+        if (errors > 0) {
+            throw new RuntimeException(errors + " errors found in ExactArithTests.");
+        }
+    }
+
+    static void fail(String message) {
+        errors++;
+        System.err.println(message);
+    }
+
+    /**
+     * Test StrictMath.addExact, multiplyExact, subtractExact, toIntValue methods
+     * with {@code int} arguments.
+     */
+    static void testIntegerExact() {
+        testIntegerExact(0, 0);
+        testIntegerExact(1, 1);
+        testIntegerExact(1, -1);
+        testIntegerExact(-1, 1);
+        testIntegerExact(1000, 2000);
+
+        testIntegerExact(Integer.MIN_VALUE, Integer.MIN_VALUE);
+        testIntegerExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testIntegerExact(Integer.MIN_VALUE, 1);
+        testIntegerExact(Integer.MAX_VALUE, 1);
+        testIntegerExact(Integer.MIN_VALUE, 2);
+        testIntegerExact(Integer.MAX_VALUE, 2);
+        testIntegerExact(Integer.MIN_VALUE, -1);
+        testIntegerExact(Integer.MAX_VALUE, -1);
+        testIntegerExact(Integer.MIN_VALUE, -2);
+        testIntegerExact(Integer.MAX_VALUE, -2);
+
+    }
+
+    /**
+     * Test exact arithmetic by comparing with the same operations using long
+     * and checking that the result is the same as the integer truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testIntegerExact(int x, int y) {
+        try {
+            // Test addExact
+            int sum = StrictMath.addExact(x, y);
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 != sum2) {
+                fail("FAIL: int StrictMath.addExact(" + x + " + " + y + ") = " + sum + "; expected Arithmetic exception");
+            } else if (sum != sum2) {
+                fail("FAIL: long StrictMath.addExact(" + x + " + " + y + ") = " + sum + "; expected: " + sum2);
+            }
+        } catch (ArithmeticException ex) {
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 == sum2) {
+                fail("FAIL: int StrictMath.addExact(" + x + " + " + y + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+
+        try {
+            // Test subtractExact
+            int diff = StrictMath.subtractExact(x, y);
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 != diff2) {
+                fail("FAIL: int StrictMath.subtractExact(" + x + " - " + y + ") = " + diff + "; expected: " + diff2);
+            }
+
+        } catch (ArithmeticException ex) {
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 == diff2) {
+                fail("FAIL: int StrictMath.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            int product = StrictMath.multiplyExact(x, y);
+            long m2 = (long) x * (long) y;
+            if ((int) m2 != m2) {
+                fail("FAIL: int StrictMath.multiplyExact(" + x + " * " + y + ") = " + product + "; expected: " + m2);
+            }
+        } catch (ArithmeticException ex) {
+            long m2 = (long) x * (long) y;
+            if ((int) m2 == m2) {
+                fail("FAIL: int StrictMath.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Test StrictMath.addExact, multiplyExact, subtractExact, toIntExact methods
+     * with {@code long} arguments.
+     */
+    static void testLongExact() {
+        testLongExactTwice(0, 0);
+        testLongExactTwice(1, 1);
+        testLongExactTwice(1, -1);
+        testLongExactTwice(1000, 2000);
+
+        testLongExactTwice(Long.MIN_VALUE, Long.MIN_VALUE);
+        testLongExactTwice(Long.MAX_VALUE, Long.MAX_VALUE);
+        testLongExactTwice(Long.MIN_VALUE, 1);
+        testLongExactTwice(Long.MAX_VALUE, 1);
+        testLongExactTwice(Long.MIN_VALUE, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Long.MIN_VALUE, -1);
+        testLongExactTwice(Long.MAX_VALUE, -1);
+        testLongExactTwice(Long.MIN_VALUE, -2);
+        testLongExactTwice(Long.MAX_VALUE, -2);
+        testLongExactTwice(Long.MIN_VALUE/2, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE, -Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE+1, Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MAX_VALUE+1, -Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MIN_VALUE-1, Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE-1, -Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE/2, 2);
+
+    }
+
+    /**
+     * Test each of the exact operations with the arguments and
+     * with the arguments reversed.
+     * @param x
+     * @param y
+     */
+    static void testLongExactTwice(long x, long y) {
+        testLongExact(x, y);
+        testLongExact(y, x);
+    }
+
+
+    /**
+     * Test long exact arithmetic by comparing with the same operations using BigInteger
+     * and checking that the result is the same as the long truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testLongExact(long x, long y) {
+        BigInteger resultBig = null;
+        final BigInteger xBig = BigInteger.valueOf(x);
+        final BigInteger yBig = BigInteger.valueOf(y);
+        try {
+            // Test addExact
+            resultBig = xBig.add(yBig);
+            long sum = StrictMath.addExact(x, y);
+            checkResult("long StrictMath.addExact", x, y, sum, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.addExact(" + x + " + " + y + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test subtractExact
+            resultBig = xBig.subtract(yBig);
+            long diff = StrictMath.subtractExact(x, y);
+            checkResult("long StrictMath.subtractExact", x, y, diff, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            resultBig = xBig.multiply(yBig);
+            long product = StrictMath.multiplyExact(x, y);
+            checkResult("long StrictMath.multiplyExact", x, y, product, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test toIntExact
+            int value = StrictMath.toIntExact(x);
+            if ((long)value != x) {
+                fail("FAIL: " + "long StrictMath.toIntExact" + "(" + x + ") = " + value + "; expected an arithmetic exception: ");
+            }
+        } catch (ArithmeticException ex) {
+            if (resultBig.bitLength() <= 32) {
+                fail("FAIL: long StrictMath.toIntExact(" + x + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Compare the expected and actual results.
+     * @param message message for the error
+     * @param x first argument
+     * @param y second argument
+     * @param result actual result value
+     * @param expected expected result value
+     */
+    static void checkResult(String message, long x, long y, long result, BigInteger expected) {
+        BigInteger resultBig = BigInteger.valueOf(result);
+        if (!inLongRange(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected an arithmetic exception: ");
+        } else if (!resultBig.equals(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected " + expected);
+        }
+    }
+
+    /**
+     * Check if the value fits in 64 bits (a long).
+     * @param value
+     * @return true if the value fits in 64 bits (including the sign).
+     */
+    static boolean inLongRange(BigInteger value) {
+        return value.bitLength() <= 63;
+    }
+}