--- a/jdk/src/share/classes/java/lang/StrictMath.java Fri Dec 11 17:18:38 2009 -0800
+++ b/jdk/src/share/classes/java/lang/StrictMath.java Tue Dec 15 13:51:44 2009 -0800
@@ -26,6 +26,7 @@
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 @@
* 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 @@
* 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/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();
+ }
+ }
+}