6430675: Math.round has surprising behavior for 0x1.fffffffffffffp-2
authordarcy
Thu, 14 Apr 2011 21:27:10 -0700
changeset 9269 f66626469aa8
parent 9268 0fee35a7b071
child 9270 499a73f29347
child 9273 da8c6a765bc9
6430675: Math.round has surprising behavior for 0x1.fffffffffffffp-2 Reviewed-by: alanb
jdk/src/share/classes/java/lang/Math.java
jdk/src/share/classes/java/lang/StrictMath.java
jdk/test/java/lang/Math/RoundTests.java
--- 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;
+    }
+}