# HG changeset patch # User sherman # Date 1329421400 28800 # Node ID 646e7e50c2d798f4a70ded6a585e1bd750741056 # Parent f0eca4f34170ff5641b11384294d68d3b9f17fc7 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 diff -r f0eca4f34170 -r 646e7e50c2d7 jdk/src/share/classes/java/lang/Math.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. * + *

+ * 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{@code scaleFactor} 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{@code scaleFactor} rounded as if performed * by a single correctly rounded floating-point multiply to a * member of the float value set. See the Java diff -r f0eca4f34170 -r 646e7e50c2d7 jdk/src/share/classes/java/lang/StrictMath.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}. * + *

+ * 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. * diff -r f0eca4f34170 -r 646e7e50c2d7 jdk/test/java/lang/Math/ExactArithTests.java --- /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; + } +} diff -r f0eca4f34170 -r 646e7e50c2d7 jdk/test/java/lang/StrictMath/ExactArithTests.java --- /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; + } +}