6430675: Math.round has surprising behavior for 0x1.fffffffffffffp-2
Reviewed-by: alanb
--- a/jdk/src/share/classes/java/lang/Math.java Thu Apr 14 12:40:30 2011 +0800
+++ b/jdk/src/share/classes/java/lang/Math.java Thu Apr 14 21:27:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2011, 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
@@ -627,11 +627,9 @@
}
/**
- * Returns the closest {@code int} to the argument. The
- * result is rounded to an integer by adding 1/2, taking the
- * floor of the result, and casting the result to type {@code int}.
- * In other words, the result is equal to the value of the expression:
- * <p>{@code (int)Math.floor(a + 0.5f)}
+ * Returns the closest {@code int} to the argument, with ties
+ * rounding up.
+ *
* <p>
* Special cases:
* <ul><li>If the argument is NaN, the result is 0.
@@ -649,17 +647,17 @@
* @see java.lang.Integer#MIN_VALUE
*/
public static int round(float a) {
- return (int)floor(a + 0.5f);
+ if (a != 0x1.fffffep-2f) // greatest float value less than 0.5
+ return (int)floor(a + 0.5f);
+ else
+ return 0;
}
/**
- * Returns the closest {@code long} to the argument. The result
- * is rounded to an integer by adding 1/2, taking the floor of the
- * result, and casting the result to type {@code long}. In other
- * words, the result is equal to the value of the expression:
- * <p>{@code (long)Math.floor(a + 0.5d)}
- * <p>
- * Special cases:
+ * Returns the closest {@code long} to the argument, with ties
+ * rounding up.
+ *
+ * <p>Special cases:
* <ul><li>If the argument is NaN, the result is 0.
* <li>If the argument is negative infinity or any value less than or
* equal to the value of {@code Long.MIN_VALUE}, the result is
@@ -676,7 +674,10 @@
* @see java.lang.Long#MIN_VALUE
*/
public static long round(double a) {
- return (long)floor(a + 0.5d);
+ if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5
+ return (long)floor(a + 0.5d);
+ else
+ return 0;
}
private static Random randomNumberGenerator;
--- a/jdk/src/share/classes/java/lang/StrictMath.java Thu Apr 14 12:40:30 2011 +0800
+++ b/jdk/src/share/classes/java/lang/StrictMath.java Thu Apr 14 21:27:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2011, 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
@@ -613,11 +613,8 @@
public static native double pow(double a, double b);
/**
- * Returns the closest {@code int} to the argument. The
- * result is rounded to an integer by adding 1/2, taking the
- * floor of the result, and casting the result to type {@code int}.
- * In other words, the result is equal to the value of the expression:
- * <p>{@code (int)Math.floor(a + 0.5f)}
+ * Returns the closest {@code int} to the argument, with ties
+ * rounding up.
*
* <p>Special cases:
* <ul><li>If the argument is NaN, the result is 0.
@@ -635,15 +632,12 @@
* @see java.lang.Integer#MIN_VALUE
*/
public static int round(float a) {
- return (int)floor(a + 0.5f);
+ return Math.round(a);
}
/**
- * Returns the closest {@code long} to the argument. The result
- * is rounded to an integer by adding 1/2, taking the floor of the
- * result, and casting the result to type {@code long}. In other
- * words, the result is equal to the value of the expression:
- * <p>{@code (long)Math.floor(a + 0.5d)}
+ * Returns the closest {@code long} to the argument, with ties
+ * rounding up.
*
* <p>Special cases:
* <ul><li>If the argument is NaN, the result is 0.
@@ -662,7 +656,7 @@
* @see java.lang.Long#MIN_VALUE
*/
public static long round(double a) {
- return (long)floor(a + 0.5d);
+ return Math.round(a);
}
private static Random randomNumberGenerator;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Math/RoundTests.java Thu Apr 14 21:27:10 2011 -0700
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+/*
+ * @test
+ * @bug 6430675
+ * @summary Check for correct implementation of {Math, StrictMath}.round
+ */
+public class RoundTests {
+ public static void main(String... args) {
+ int failures = 0;
+
+ failures += testNearFloatHalfCases();
+ failures += testNearDoubleHalfCases();
+
+ if (failures > 0) {
+ System.err.println("Testing {Math, StrictMath}.round incurred "
+ + failures + " failures.");
+ throw new RuntimeException();
+ }
+ }
+
+ private static int testNearDoubleHalfCases() {
+ int failures = 0;
+ double [][] testCases = {
+ {+0x1.fffffffffffffp-2, 0.0},
+ {+0x1.0p-1, 1.0}, // +0.5
+ {+0x1.0000000000001p-1, 1.0},
+
+ {-0x1.fffffffffffffp-2, 0.0},
+ {-0x1.0p-1, 0.0}, // -0.5
+ {-0x1.0000000000001p-1, -1.0},
+ };
+
+ for(double[] testCase : testCases) {
+ failures += testNearHalfCases(testCase[0], (long)testCase[1]);
+ }
+
+ return failures;
+ }
+
+ private static int testNearHalfCases(double input, double expected) {
+ int failures = 0;
+
+ failures += Tests.test("Math.round", input, Math.round(input), expected);
+ failures += Tests.test("StrictMath.round", input, StrictMath.round(input), expected);
+
+ return failures;
+ }
+
+ private static int testNearFloatHalfCases() {
+ int failures = 0;
+ float [][] testCases = {
+ {+0x1.fffffep-2f, 0.0f},
+ {+0x1.0p-1f, 1.0f}, // +0.5
+ {+0x1.000002p-1f, 1.0f},
+
+ {-0x1.fffffep-2f, 0.0f},
+ {-0x1.0p-1f, 0.0f}, // -0.5
+ {-0x1.000002p-1f, -1.0f},
+ };
+
+ for(float[] testCase : testCases) {
+ failures += testNearHalfCases(testCase[0], (int)testCase[1]);
+ }
+
+ return failures;
+ }
+
+ private static int testNearHalfCases(float input, float expected) {
+ int failures = 0;
+
+ failures += Tests.test("Math.round", input, Math.round(input), expected);
+ failures += Tests.test("StrictMath.round", input, StrictMath.round(input), expected);
+
+ return failures;
+ }
+}