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
--- 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} ×
+ * Returns {@code d} ×
* 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} ×
+ * Returns {@code f} ×
* 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;
+ }
+}