United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6908131 Pure Java implementations of java.lang.StrictMath.floor(double) & java.lang.StrictMath.ceil(double)
JDK-6908131 : Pure Java implementations of java.lang.StrictMath.floor(double) & java.lang.StrictMath.ceil(double)

Details
Type:
Enhancement
Submit Date:
2009-12-07
Status:
Resolved
Updated Date:
2010-07-09
Project Name:
JDK
Resolved Date:
2010-01-08
Component:
core-libs
OS:
generic
Sub-Component:
java.lang
CPU:
generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
6u17
Fixed Versions:

Related Reports
Backport:
Relates:
Relates:
Relates:

Sub Tasks

Description
On workloads / programs which make very frequent calls to java.lang.StrictMath.floor(double) and java.lang.StrictMath(double) may exhibit symptoms of high sys CPU usage as a result of frequent safepoint polling which occur when crossing the JNI to Java boundary.

This safepoint polling, exhibiting sys CPU usage could be elided by implementing pure Java implementations of java.lang.StrictMath.floor(double) and java.lang.StrictMath.ceil(double).

                                    

Comments
EVALUATION

A fine idea.
                                     
2009-12-08
SUGGESTED FIX

# HG changeset patch
# User darcy
# Date 1260913904 28800
# Node ID ad1e30930c6c34e95f9aea0bcb5d8bb8978ed6ed
# Parent 15568b6998f4552b8aa4d4f32a9797483fa90a20
6908131: Pure Java implementations of StrictMath.floor(double) & StrictMath.ceil(double)
Reviewed-by: alanb

--- a/make/java/java/mapfile-vers	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/mapfile-vers	Tue Dec 15 13:51:44 2009 -0800
@@ -150,10 +150,8 @@ SUNWprivate_1.1 {
 		Java_java_lang_StrictMath_asin;
 		Java_java_lang_StrictMath_atan;
 		Java_java_lang_StrictMath_atan2;
-		Java_java_lang_StrictMath_ceil;
 		Java_java_lang_StrictMath_cos;
 		Java_java_lang_StrictMath_exp;
-		Java_java_lang_StrictMath_floor;
 		Java_java_lang_StrictMath_log;
 		Java_java_lang_StrictMath_log10;
 		Java_java_lang_StrictMath_pow;
--- a/make/java/java/reorder-i586	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/reorder-i586	Tue Dec 15 13:51:44 2009 -0800
@@ -107,4 +107,3 @@ text: .text%Java_java_util_TimeZone_getS
 text: .text%Java_java_util_TimeZone_getSystemTimeZoneID;
 text: .text%findJavaTZ_md;
 text: .text%Java_java_lang_StrictMath_log;
-text: .text%Java_java_lang_StrictMath_floor;
--- a/make/java/java/reorder-sparc	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/reorder-sparc	Tue Dec 15 13:51:44 2009 -0800
@@ -105,4 +105,3 @@ text: .text%findJavaTZ_md;
 text: .text%findJavaTZ_md;
 text: .text%Java_java_lang_StrictMath_log;
 text: .text%Java_java_lang_StrictMath_sqrt;
-text: .text%Java_java_lang_StrictMath_floor;
--- a/make/java/java/reorder-sparcv9	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/reorder-sparcv9	Tue Dec 15 13:51:44 2009 -0800
@@ -101,4 +101,3 @@ text: .text%findJavaTZ_md;
 text: .text%findJavaTZ_md;
 text: .text%Java_java_lang_StrictMath_log;
 text: .text%Java_java_lang_StrictMath_sqrt;
-text: .text%Java_java_lang_StrictMath_floor;
--- a/src/share/classes/java/lang/StrictMath.java	Fri Dec 11 17:18:38 2009 -0800
+++ b/src/share/classes/java/lang/StrictMath.java	Tue Dec 15 13:51:44 2009 -0800
@@ -26,6 +26,7 @@ package java.lang;
 package java.lang;
 import java.util.Random;
 import sun.misc.FpUtils;
+import sun.misc.DoubleConsts;
 
 /**
  * The class {@code StrictMath} contains methods for performing basic
@@ -316,7 +317,9 @@ public final class StrictMath {
      *          floating-point value that is greater than or equal to
      *          the argument and is equal to a mathematical integer.
      */
-    public static native double ceil(double a);
+    public static double ceil(double a) {
+        return floorOrCeil(a, -0.0, 1.0, 1.0);
+    }
 
     /**
      * Returns the largest (closest to positive infinity)
@@ -333,7 +336,54 @@ public final class StrictMath {
      *          floating-point value that less than or equal to the argument
      *          and is equal to a mathematical integer.
      */
-    public static native double floor(double a);
+    public static double floor(double a) {
+        return floorOrCeil(a, -1.0, 0.0, -1.0);
+    }
+
+    /**
+     * Internal method to share logic between floor and ceil.
+     *
+     * @param a the value to be floored or ceiled
+     * @param negativeBoundary result for values in (-1, 0)
+     * @param positiveBoundary result for values in (0, 1)
+     * @param increment value to add when the argument is non-integral
+     */
+    private static double floorOrCeil(double a,
+                                      double negativeBoundary,
+                                      double positiveBoundary,
+                                      double sign) {
+        int exponent = Math.getExponent(a);
+
+        if (exponent < 0) {
+            /*
+             * Absolute value of argument is less than 1.
+             * floorOrceil(-0.0) => -0.0
+             * floorOrceil(+0.0) => +0.0
+             */
+            return ((a == 0.0) ? a :
+                    ( (a < 0.0) ?  negativeBoundary : positiveBoundary) );
+        } else if (exponent >= 52) {
+            /*
+             * Infinity, NaN, or a value so large it must be integral.
+             */
+            return a;
+        }
+        // Else the argument is either an integral value already XOR it
+        // has to be rounded to one.
+        assert exponent >= 0 && exponent <= 51;
+
+        long doppel = Double.doubleToRawLongBits(a);
+        long mask   = DoubleConsts.SIGNIF_BIT_MASK >> exponent;
+
+        if ( (mask & doppel) == 0L )
+            return a; // integral value
+        else {
+            double result = Double.longBitsToDouble(doppel & (~mask));
+            if (sign*a > 0.0)
+                result = result + sign;
+            return result;
+        }
+    }
 
     /**
      * Returns the {@code double} value that is closest in value
--- a/src/share/native/java/lang/StrictMath.c	Fri Dec 11 17:18:38 2009 -0800
+++ b/src/share/native/java/lang/StrictMath.c	Tue Dec 15 13:51:44 2009 -0800
@@ -95,18 +95,6 @@ Java_java_lang_StrictMath_cbrt(JNIEnv *e
 }
 
 JNIEXPORT jdouble JNICALL
-Java_java_lang_StrictMath_ceil(JNIEnv *env, jclass unused, jdouble d)
-{
-    return (jdouble) jceil((double)d);
-}
-
-JNIEXPORT jdouble JNICALL
-Java_java_lang_StrictMath_floor(JNIEnv *env, jclass unused, jdouble d)
-{
-    return (jdouble) jfloor((double)d);
-}
-
-JNIEXPORT jdouble JNICALL
 Java_java_lang_StrictMath_atan2(JNIEnv *env, jclass unused, jdouble d1, jdouble d2)
 {
     return (jdouble) jatan2((double)d1, (double)d2);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/Math/CeilAndFloorTests.java	Tue Dec 15 13:51:44 2009 -0800
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6908131
+ * @summary Check for correct implementation of Math.ceil and Math.floor
+ */
+
+import sun.misc.FpUtils;
+import sun.misc.DoubleConsts;
+
+public class CeilAndFloorTests {
+    private static int testCeilCase(double input, double expected) {
+        int failures = 0;
+        failures += Tests.test("Math.ceil",  input, Math.ceil(input),   expected);
+        failures += Tests.test("StrictMath.ceil",  input, StrictMath.ceil(input), expected);
+        return failures;
+    }
+
+    private static int testFloorCase(double input, double expected) {
+        int failures = 0;
+        failures += Tests.test("Math.floor",  input, Math.floor(input),   expected);
+        failures += Tests.test("StrictMath.floor",  input, StrictMath.floor(input), expected);
+        return failures;
+    }
+
+    private static int nearIntegerTests() {
+        int failures = 0;
+
+        double [] fixedPoints = {
+            -0.0,
+             0.0,
+            -1.0,
+             1.0,
+            -0x1.0p52,
+             0x1.0p52,
+            -Double.MAX_VALUE,
+             Double.MAX_VALUE,
+             Double.NEGATIVE_INFINITY,
+             Double.POSITIVE_INFINITY,
+             Double.NaN,
+        };
+
+        for(double fixedPoint : fixedPoints) {
+            failures += testCeilCase(fixedPoint, fixedPoint);
+            failures += testFloorCase(fixedPoint, fixedPoint);
+        }
+
+        for(int i = Double.MIN_EXPONENT; i <= Double.MAX_EXPONENT; i++) {
+            double powerOfTwo   = Math.scalb(1.0, i);
+            double neighborDown = FpUtils.nextDown(powerOfTwo);
+            double neighborUp   = Math.nextUp(powerOfTwo);
+
+            if (i < 0) {
+                failures += testCeilCase( powerOfTwo,  1.0);
+                failures += testCeilCase(-powerOfTwo, -0.0);
+
+                failures += testFloorCase( powerOfTwo,  0.0);
+                failures += testFloorCase(-powerOfTwo, -1.0);
+
+                failures += testCeilCase( neighborDown, 1.0);
+                failures += testCeilCase(-neighborDown, -0.0);
+
+                failures += testFloorCase( neighborUp,  0.0);
+                failures += testFloorCase(-neighborUp, -1.0);
+            } else {
+                failures += testCeilCase(powerOfTwo, powerOfTwo);
+                failures += testFloorCase(powerOfTwo, powerOfTwo);
+
+                if (neighborDown==Math.rint(neighborDown)) {
+                    failures += testCeilCase( neighborDown,  neighborDown);
+                    failures += testCeilCase(-neighborDown, -neighborDown);
+
+                    failures += testFloorCase( neighborDown, neighborDown);
+                    failures += testFloorCase(-neighborDown,-neighborDown);
+                } else {
+                    failures += testCeilCase( neighborDown, powerOfTwo);
+                    failures += testFloorCase(-neighborDown, -powerOfTwo);
+                }
+
+                if (neighborUp==Math.rint(neighborUp)) {
+                    failures += testCeilCase(neighborUp, neighborUp);
+                    failures += testCeilCase(-neighborUp, -neighborUp);
+
+                    failures += testFloorCase(neighborUp, neighborUp);
+                    failures += testFloorCase(-neighborUp, -neighborUp);
+                } else {
+                    failures += testFloorCase(neighborUp, powerOfTwo);
+                    failures += testCeilCase(-neighborUp, -powerOfTwo);
+                }
+            }
+        }
+
+        for(int i = -(0x10000); i <= 0x10000; i++) {
+            double d = (double) i;
+            double neighborDown = FpUtils.nextDown(d);
+            double neighborUp   = Math.nextUp(d);
+
+            failures += testCeilCase( d, d);
+            failures += testCeilCase(-d, -d);
+
+            failures += testFloorCase( d, d);
+            failures += testFloorCase(-d, -d);
+
+            if (Math.abs(d) > 1.0) {
+                failures += testCeilCase( neighborDown, d);
+                failures += testCeilCase(-neighborDown, -d+1);
+
+                failures += testFloorCase( neighborUp, d);
+                failures += testFloorCase(-neighborUp, -d-1);
+            }
+        }
+
+        return failures;
+    }
+
+    public static int roundingTests() {
+        int failures = 0;
+        double [][] testCases = {
+            { Double.MIN_VALUE,                           1.0},
+            {-Double.MIN_VALUE,                          -0.0},
+            { FpUtils.nextDown(DoubleConsts.MIN_NORMAL),  1.0},
+            {-FpUtils.nextDown(DoubleConsts.MIN_NORMAL), -0.0},
+            { DoubleConsts.MIN_NORMAL,                    1.0},
+            {-DoubleConsts.MIN_NORMAL,                   -0.0},
+
+            { 0.1,                                        1.0},
+            {-0.1,                                       -0.0},
+
+            { 0.5,                                        1.0},
+            {-0.5,                                       -0.0},
+
+            { 1.5,                                        2.0},
+            {-1.5,                                       -1.0},
+
+            { 2.5,                                        3.0},
+            {-2.5,                                       -2.0},
+
+            { FpUtils.nextDown(1.0),                      1.0},
+            { FpUtils.nextDown(-1.0),                    -1.0},
+
+            { Math.nextUp(1.0),                           2.0},
+            { Math.nextUp(-1.0),                         -0.0},
+
+            { 0x1.0p51,                                 0x1.0p51},
+            {-0x1.0p51,                                -0x1.0p51},
+
+            { FpUtils.nextDown(0x1.0p51),               0x1.0p51},
+            {-Math.nextUp(0x1.0p51),                   -0x1.0p51},
+
+            { Math.nextUp(0x1.0p51),                    0x1.0p51+1},
+            {-FpUtils.nextDown(0x1.0p51),              -0x1.0p51+1},
+
+            { FpUtils.nextDown(0x1.0p52),               0x1.0p52},
+            {-Math.nextUp(0x1.0p52),                   -0x1.0p52-1.0},
+
+            { Math.nextUp(0x1.0p52),                    0x1.0p52+1.0},
+            {-FpUtils.nextDown(0x1.0p52),              -0x1.0p52+1.0},
+        };
+
+        for(double[] testCase : testCases) {
+            failures += testCeilCase(testCase[0], testCase[1]);
+            failures += testFloorCase(-testCase[0], -testCase[1]);
+        }
+        return failures;
+    }
+
+    public static void main(String... args) {
+        int failures = 0;
+
+        failures += nearIntegerTests();
+        failures += roundingTests();
+
+        if (failures > 0) {
+            System.err.println("Testing {Math, StrictMath}.ceil incurred "
+                               + failures + " failures.");
+            throw new RuntimeException();
+        }
+    }
+}
                                     
2009-12-15
PUBLIC COMMENTS

See
http://hg.openjdk.java.net/jdk7/tl/jdk/rev/ad1e30930c6c
                                     
2009-12-15
SUGGESTED FIX

# HG changeset patch
# User darcy
# Date 1260913904 28800
# Node ID ad1e30930c6c34e95f9aea0bcb5d8bb8978ed6ed
# Parent 15568b6998f4552b8aa4d4f32a9797483fa90a20
6908131: Pure Java implementations of StrictMath.floor(double) & StrictMath.ceil(double)
Reviewed-by: alanb

--- a/make/java/java/mapfile-vers	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/mapfile-vers	Tue Dec 15 13:51:44 2009 -0800
@@ -150,10 +150,8 @@ SUNWprivate_1.1 {
 		Java_java_lang_StrictMath_asin;
 		Java_java_lang_StrictMath_atan;
 		Java_java_lang_StrictMath_atan2;
-		Java_java_lang_StrictMath_ceil;
 		Java_java_lang_StrictMath_cos;
 		Java_java_lang_StrictMath_exp;
-		Java_java_lang_StrictMath_floor;
 		Java_java_lang_StrictMath_log;
 		Java_java_lang_StrictMath_log10;
 		Java_java_lang_StrictMath_pow;
--- a/make/java/java/reorder-i586	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/reorder-i586	Tue Dec 15 13:51:44 2009 -0800
@@ -107,4 +107,3 @@ text: .text%Java_java_util_TimeZone_getS
 text: .text%Java_java_util_TimeZone_getSystemTimeZoneID;
 text: .text%findJavaTZ_md;
 text: .text%Java_java_lang_StrictMath_log;
-text: .text%Java_java_lang_StrictMath_floor;
--- a/make/java/java/reorder-sparc	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/reorder-sparc	Tue Dec 15 13:51:44 2009 -0800
@@ -105,4 +105,3 @@ text: .text%findJavaTZ_md;
 text: .text%findJavaTZ_md;
 text: .text%Java_java_lang_StrictMath_log;
 text: .text%Java_java_lang_StrictMath_sqrt;
-text: .text%Java_java_lang_StrictMath_floor;
--- a/make/java/java/reorder-sparcv9	Fri Dec 11 17:18:38 2009 -0800
+++ b/make/java/java/reorder-sparcv9	Tue Dec 15 13:51:44 2009 -0800
@@ -101,4 +101,3 @@ text: .text%findJavaTZ_md;
 text: .text%findJavaTZ_md;
 text: .text%Java_java_lang_StrictMath_log;
 text: .text%Java_java_lang_StrictMath_sqrt;
-text: .text%Java_java_lang_StrictMath_floor;
--- a/src/share/classes/java/lang/StrictMath.java	Fri Dec 11 17:18:38 2009 -0800
+++ b/src/share/classes/java/lang/StrictMath.java	Tue Dec 15 13:51:44 2009 -0800
@@ -26,6 +26,7 @@ package java.lang;
 package java.lang;
 import java.util.Random;
 import sun.misc.FpUtils;
+import sun.misc.DoubleConsts;
 
 /**
  * The class {@code StrictMath} contains methods for performing basic
@@ -316,7 +317,9 @@ public final class StrictMath {
      *          floating-point value that is greater than or equal to
      *          the argument and is equal to a mathematical integer.
      */
-    public static native double ceil(double a);
+    public static double ceil(double a) {
+        return floorOrCeil(a, -0.0, 1.0, 1.0);
+    }
 
     /**
      * Returns the largest (closest to positive infinity)
@@ -333,7 +336,54 @@ public final class StrictMath {
      *          floating-point value that less than or equal to the argument
      *          and is equal to a mathematical integer.
      */
-    public static native double floor(double a);
+    public static double floor(double a) {
+        return floorOrCeil(a, -1.0, 0.0, -1.0);
+    }
+
+    /**
+     * Internal method to share logic between floor and ceil.
+     *
+     * @param a the value to be floored or ceiled
+     * @param negativeBoundary result for values in (-1, 0)
+     * @param positiveBoundary result for values in (0, 1)
+     * @param increment value to add when the argument is non-integral
+     */
+    private static double floorOrCeil(double a,
+                                      double negativeBoundary,
+                                      double positiveBoundary,
+                                      double sign) {
+        int exponent = Math.getExponent(a);
+
+        if (exponent < 0) {
+            /*
+             * Absolute value of argument is less than 1.
+             * floorOrceil(-0.0) => -0.0
+             * floorOrceil(+0.0) => +0.0
+             */
+            return ((a == 0.0) ? a :
+                    ( (a < 0.0) ?  negativeBoundary : positiveBoundary) );
+        } else if (exponent >= 52) {
+            /*
+             * Infinity, NaN, or a value so large it must be integral.
+             */
+            return a;
+        }
+        // Else the argument is either an integral value already XOR it
+        // has to be rounded to one.
+        assert exponent >= 0 && exponent <= 51;
+
+        long doppel = Double.doubleToRawLongBits(a);
+        long mask   = DoubleConsts.SIGNIF_BIT_MASK >> exponent;
+
+        if ( (mask & doppel) == 0L )
+            return a; // integral value
+        else {
+            double result = Double.longBitsToDouble(doppel & (~mask));
+            if (sign*a > 0.0)
+                result = result + sign;
+            return result;
+        }
+    }
 
     /**
      * Returns the {@code double} value that is closest in value
--- a/src/share/native/java/lang/StrictMath.c	Fri Dec 11 17:18:38 2009 -0800
+++ b/src/share/native/java/lang/StrictMath.c	Tue Dec 15 13:51:44 2009 -0800
@@ -95,18 +95,6 @@ Java_java_lang_StrictMath_cbrt(JNIEnv *e
 }
 
 JNIEXPORT jdouble JNICALL
-Java_java_lang_StrictMath_ceil(JNIEnv *env, jclass unused, jdouble d)
-{
-    return (jdouble) jceil((double)d);
-}
-
-JNIEXPORT jdouble JNICALL
-Java_java_lang_StrictMath_floor(JNIEnv *env, jclass unused, jdouble d)
-{
-    return (jdouble) jfloor((double)d);
-}
-
-JNIEXPORT jdouble JNICALL
 Java_java_lang_StrictMath_atan2(JNIEnv *env, jclass unused, jdouble d1, jdouble d2)
 {
     return (jdouble) jatan2((double)d1, (double)d2);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/Math/CeilAndFloorTests.java	Tue Dec 15 13:51:44 2009 -0800
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6908131
+ * @summary Check for correct implementation of Math.ceil and Math.floor
+ */
+
+import sun.misc.FpUtils;
+import sun.misc.DoubleConsts;
+
+public class CeilAndFloorTests {
+    private static int testCeilCase(double input, double expected) {
+        int failures = 0;
+        failures += Tests.test("Math.ceil",  input, Math.ceil(input),   expected);
+        failures += Tests.test("StrictMath.ceil",  input, StrictMath.ceil(input), expected);
+        return failures;
+    }
+
+    private static int testFloorCase(double input, double expected) {
+        int failures = 0;
+        failures += Tests.test("Math.floor",  input, Math.floor(input),   expected);
+        failures += Tests.test("StrictMath.floor",  input, StrictMath.floor(input), expected);
+        return failures;
+    }
+
+    private static int nearIntegerTests() {
+        int failures = 0;
+
+        double [] fixedPoints = {
+            -0.0,
+             0.0,
+            -1.0,
+             1.0,
+            -0x1.0p52,
+             0x1.0p52,
+            -Double.MAX_VALUE,
+             Double.MAX_VALUE,
+             Double.NEGATIVE_INFINITY,
+             Double.POSITIVE_INFINITY,
+             Double.NaN,
+        };
+
+        for(double fixedPoint : fixedPoints) {
+            failures += testCeilCase(fixedPoint, fixedPoint);
+            failures += testFloorCase(fixedPoint, fixedPoint);
+        }
+
+        for(int i = Double.MIN_EXPONENT; i <= Double.MAX_EXPONENT; i++) {
+            double powerOfTwo   = Math.scalb(1.0, i);
+            double neighborDown = FpUtils.nextDown(powerOfTwo);
+            double neighborUp   = Math.nextUp(powerOfTwo);
+
+            if (i < 0) {
+                failures += testCeilCase( powerOfTwo,  1.0);
+                failures += testCeilCase(-powerOfTwo, -0.0);
+
+                failures += testFloorCase( powerOfTwo,  0.0);
+                failures += testFloorCase(-powerOfTwo, -1.0);
+
+                failures += testCeilCase( neighborDown, 1.0);
+                failures += testCeilCase(-neighborDown, -0.0);
+
+                failures += testFloorCase( neighborUp,  0.0);
+                failures += testFloorCase(-neighborUp, -1.0);
+            } else {
+                failures += testCeilCase(powerOfTwo, powerOfTwo);
+                failures += testFloorCase(powerOfTwo, powerOfTwo);
+
+                if (neighborDown==Math.rint(neighborDown)) {
+                    failures += testCeilCase( neighborDown,  neighborDown);
+                    failures += testCeilCase(-neighborDown, -neighborDown);
+
+                    failures += testFloorCase( neighborDown, neighborDown);
+                    failures += testFloorCase(-neighborDown,-neighborDown);
+                } else {
+                    failures += testCeilCase( neighborDown, powerOfTwo);
+                    failures += testFloorCase(-neighborDown, -powerOfTwo);
+                }
+
+                if (neighborUp==Math.rint(neighborUp)) {
+                    failures += testCeilCase(neighborUp, neighborUp);
+                    failures += testCeilCase(-neighborUp, -neighborUp);
+
+                    failures += testFloorCase(neighborUp, neighborUp);
+                    failures += testFloorCase(-neighborUp, -neighborUp);
+                } else {
+                    failures += testFloorCase(neighborUp, powerOfTwo);
+                    failures += testCeilCase(-neighborUp, -powerOfTwo);
+                }
+            }
+        }
+
+        for(int i = -(0x10000); i <= 0x10000; i++) {
+            double d = (double) i;
+            double neighborDown = FpUtils.nextDown(d);
+            double neighborUp   = Math.nextUp(d);
+
+            failures += testCeilCase( d, d);
+            failures += testCeilCase(-d, -d);
+
+            failures += testFloorCase( d, d);
+            failures += testFloorCase(-d, -d);
+
+            if (Math.abs(d) > 1.0) {
+                failures += testCeilCase( neighborDown, d);
+                failures += testCeilCase(-neighborDown, -d+1);
+
+                failures += testFloorCase( neighborUp, d);
+                failures += testFloorCase(-neighborUp, -d-1);
+            }
+        }
+
+        return failures;
+    }
+
+    public static int roundingTests() {
+        int failures = 0;
+        double [][] testCases = {
+            { Double.MIN_VALUE,                           1.0},
+            {-Double.MIN_VALUE,                          -0.0},
+            { FpUtils.nextDown(DoubleConsts.MIN_NORMAL),  1.0},
+            {-FpUtils.nextDown(DoubleConsts.MIN_NORMAL), -0.0},
+            { DoubleConsts.MIN_NORMAL,                    1.0},
+            {-DoubleConsts.MIN_NORMAL,                   -0.0},
+
+            { 0.1,                                        1.0},
+            {-0.1,                                       -0.0},
+
+            { 0.5,                                        1.0},
+            {-0.5,                                       -0.0},
+
+            { 1.5,                                        2.0},
+            {-1.5,                                       -1.0},
+
+            { 2.5,                                        3.0},
+            {-2.5,                                       -2.0},
+
+            { FpUtils.nextDown(1.0),                      1.0},
+            { FpUtils.nextDown(-1.0),                    -1.0},
+
+            { Math.nextUp(1.0),                           2.0},
+            { Math.nextUp(-1.0),                         -0.0},
+
+            { 0x1.0p51,                                 0x1.0p51},
+            {-0x1.0p51,                                -0x1.0p51},
+
+            { FpUtils.nextDown(0x1.0p51),               0x1.0p51},
+            {-Math.nextUp(0x1.0p51),                   -0x1.0p51},
+
+            { Math.nextUp(0x1.0p51),                    0x1.0p51+1},
+            {-FpUtils.nextDown(0x1.0p51),              -0x1.0p51+1},
+
+            { FpUtils.nextDown(0x1.0p52),               0x1.0p52},
+            {-Math.nextUp(0x1.0p52),                   -0x1.0p52-1.0},
+
+            { Math.nextUp(0x1.0p52),                    0x1.0p52+1.0},
+            {-FpUtils.nextDown(0x1.0p52),              -0x1.0p52+1.0},
+        };
+
+        for(double[] testCase : testCases) {
+            failures += testCeilCase(testCase[0], testCase[1]);
+            failures += testFloorCase(-testCase[0], -testCase[1]);
+        }
+        return failures;
+    }
+
+    public static void main(String... args) {
+        int failures = 0;
+
+        failures += nearIntegerTests();
+        failures += roundingTests();
+
+        if (failures > 0) {
+            System.err.println("Testing {Math, StrictMath}.ceil incurred "
+                               + failures + " failures.");
+            throw new RuntimeException();
+        }
+    }
+}
                                     
2009-12-15



Hardware and Software, Engineered to Work Together