JDK-6908131 : Pure Java implementations of java.lang.StrictMath.floor(double) & java.lang.StrictMath.ceil(double)
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6u17
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2009-12-07
  • Updated: 2015-09-01
  • Resolved: 2010-01-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u21Fixed 7 b79Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
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
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(); + } + } +}
15-12-2009

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

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(); + } + } +}
15-12-2009

EVALUATION A fine idea.
08-12-2009