Implementation for Double.longBitsToDouble and Double.doubleToLongBits Patch by: Ray Cromwell (cromwellian@gmail.com) Review by: fabbott git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2421 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/lang/Double.java b/user/super/com/google/gwt/emul/java/lang/Double.java index ce65260..16fd322 100644 --- a/user/super/com/google/gwt/emul/java/lang/Double.java +++ b/user/super/com/google/gwt/emul/java/lang/Double.java
@@ -22,7 +22,7 @@ public static final double MAX_VALUE = 1.7976931348623157e+308; public static final double MIN_VALUE = 4.9e-324; public static final double MIN_NORMAL = 2.2250738585072014e-308; - public static final int MAX_EXPONENT = 1023; + public static final int MAX_EXPONENT = 1023; // ==Math.getExponent(Double.MAX_VALUE); public static final int MIN_EXPONENT = -1022; // ==Math.getExponent(Double.MIN_NORMAL);; @@ -31,6 +31,26 @@ public static final double NEGATIVE_INFINITY = -1d / 0d; public static final double POSITIVE_INFINITY = 1d / 0d; public static final int SIZE = 64; + static final int EXPONENT_BITSIZE = 11; + // the extra -1 is for the sign bit + static final int MANTISSA_BITSIZE = SIZE - EXPONENT_BITSIZE + - 1; + // the exponent is biased by one less than its midpoint, e.g. 2^11 / 2 - 1; + static final int EXPONENT_BIAS = 1 << (EXPONENT_BITSIZE - 1) - 1; + // the mask is all 1 bits in the exponent, e.g. 0x7ff shifted over by 52 + static final long EXPONENT_MASK = (1L + << EXPONENT_BITSIZE - 1) << MANTISSA_BITSIZE; + // place 1-bit in top position + static final long NAN_MANTISSA = 1L << (MANTISSA_BITSIZE - 1); + // sign bit is the MSB bit + static final long SIGN_BIT = 0x1L << (SIZE - 1); + // Zero represented in biased form + static final int BIASED_ZERO_EXPONENT = EXPONENT_BIAS; + // The maximum mantissa value, represented as a double + static final double MAX_MANTISSA_VALUE = Math + .pow(2, MANTISSA_BITSIZE); + // The mantissa of size MANTISSA_BITSIZE with all bits set to 1_ + static final long MANTISSA_MASK = (1L << MANTISSA_BITSIZE) - 1; public static int compare(double x, double y) { if (x < y) { @@ -42,6 +62,83 @@ } } + // Theory of operation: Let a double number d be represented as + // 1.M * 2^E, where the leading bit is assumed to be 1, + // the fractional mantissa M is multiplied 2 to the power of E. + // We want to reliably recover M and E, and then encode them according + // to IEEE754 (see http://en.wikipedia.org/wiki/IEEE754) + public static long doubleToLongBits(final double d) { + + long sign = (d < 0 ? SIGN_BIT : 0); + long exponent = 0; + double absV = Math.abs(d); + + if (Double.isNaN(d)) { + // IEEE754, NaN exponent bits all 1s, and mantissa is non-zero + return EXPONENT_MASK | NAN_MANTISSA; + } + if (Double.isInfinite(d)) { + // an infinite number is a number with a zero mantissa and all + // exponent bits set to 1 + exponent = EXPONENT_MASK; + absV = 0.0; + } else { + if (absV == 0.0) { + // IEEE754, exponent is 0, mantissa is zero + // we don't handle negative zero at the moment, it is treated as + // positive zero + exponent = 0L; + } else { + // get an approximation to the exponent + // if d = 1.M * 2^E then + // log2(d) = log(1.M) + log2(2^E) = log(1.M) + E + // floor(log(1.M) + E) = E because log(1.M) always < 1 + // it may turn out log2(x) = log(x)/log(2) always returns the + // the correct exponent, but this method is more defensive + // with respect to precision to avoid off by 1 problems + int guess = (int) Math.floor(Math.log(absV) / Math.log(2)); + // force it to MAX_EXPONENT, MAX_EXPONENT interval + // (<= -MAX_EXPONENT = denorm/zero) + guess = Math.max(-MAX_EXPONENT, Math.min(guess, MAX_EXPONENT)); + + // Recall that d = 1.M * 2^E, so dividing by 2^E should leave + // us with 1.M + double exp = Math.pow(2, guess); + absV = absV / exp; + + // while the number is still bigger than a normalized number + // increment exponent guess + // This might occur if there is some precision loss in determining + // the exponent + while (absV > 2.0) { + guess++; + absV /= 2.0; + } + // if the number is smaller than a normalized number + // decrement exponent. If the exponent becomes zero, and we + // fail to achieve a normalized mantissa, then this number + // must be a denormalized value + while (absV < 1 && guess > 0) { + guess--; + absV *= 2; + } + exponent = (guess + EXPONENT_BIAS) << MANTISSA_BITSIZE; + } + } + // if denormalized + if (exponent <= BIASED_ZERO_EXPONENT) { + // denormalized numbers have an exponent of zero, but pretend + // they have an exponent of 1, so since there is an implicit + // * 2^1 for denorms, we correct by dividing by 2 + absV /= 2; + } + // the input value has now been stripped of its exponent + // and is in the range [1,2), we strip off the leading decimal to normalize + // and use the remainer as a percentage of the significand value (2^52) + long mantissa = (long) ((absV % 1) * MAX_MANTISSA_VALUE); + return sign | exponent | (mantissa & MANTISSA_MASK); + } + /** * @skip Here for shared implementation with Arrays.hashCode */ @@ -57,6 +154,33 @@ return isNaN(x); }-*/; + public static double longBitsToDouble(long value) { + // exponent in MSB bits 1-11 + int exp = (int) ((value & EXPONENT_MASK) >> MANTISSA_BITSIZE); + // unbias exponent handle denorm case + int denorm = (exp == 0 ? 1 : 0); + // denorm exponent becomes -1022 + exp = exp - EXPONENT_BIAS + denorm; + // mantissa in LSB 52 bits + long mantissa = (value & MANTISSA_MASK); + // sign in MSB bit 0 + int sign = (value & SIGN_BIT) != 0 ? -1 : 1; + // unbiased exponent value of EXPONENT_BIAS + 1 (e.g. 1024) + // is equivalent to all 1 bits in biased exp (e.g. 2047) + if (exp == EXPONENT_BIAS + 1) { + if (mantissa != 0) { + return Double.NaN; + } else { + return sign < 0 ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + } + } + // non-denormized numbers get 1.0 added back, since our first digit is + // always a 1 + // mantissa is divided by 2^52, and multiplied by 2^exponent + return sign * ((mantissa / MAX_MANTISSA_VALUE + (1 - denorm)) * Math + .pow(2, exp)); + } + public static double parseDouble(String s) throws NumberFormatException { return __parseAndValidateDouble(s); }
diff --git a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java index 283763b..728ce1c 100644 --- a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java +++ b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
@@ -23,6 +23,17 @@ */ public class DoubleTest extends GWTTestCase { + // Some actual results from JDK1.6 VM doubleToLongBits calls + private static final long NAN_LONG_VALUE = 0x7ff8000000000000L; + private static final long POSINF_LONG_VALUE = 0x7ff0000000000000L; + private static final long NEGINF_LONG_VALUE = 0xfff0000000000000L; + private static final long MAXD_LONG_VALUE = 0x7fefffffffffffffL; + private static final long MIND_LONG_VALUE = 0x1L; + private static final long MINNORM_LONG_VALUE = 0x10000000000000L; + private static final double TEST1_DOUBLE_VALUE = 2.3e27; + private static final long TEST1_LONG_VALUE = 0x459dba0fc757e49cL; + private static final long NEGTEST1_LONG_VALUE = 0xc59dba0fc757e49cL; + public String getModuleName() { return "com.google.gwt.emultest.EmulSuite"; } @@ -64,6 +75,31 @@ // Double.MIN_EXPONENT); } + public void testDoubleToLongBits() { + assertEquals(Double.doubleToLongBits(Double.NaN), NAN_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.POSITIVE_INFINITY), POSINF_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.NEGATIVE_INFINITY), NEGINF_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.MAX_VALUE), MAXD_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.MIN_VALUE), MIND_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.MIN_NORMAL), MINNORM_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.MAX_VALUE), MAXD_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.MIN_VALUE), MIND_LONG_VALUE); + assertEquals(Double.doubleToLongBits(Double.MIN_NORMAL), MINNORM_LONG_VALUE); + assertEquals(Double.doubleToLongBits(TEST1_DOUBLE_VALUE), TEST1_LONG_VALUE); + assertEquals(Double.doubleToLongBits(-TEST1_DOUBLE_VALUE), NEGTEST1_LONG_VALUE); + } + + public void testLongBitsToDouble() { + assertTrue(Double.isNaN(Double.longBitsToDouble(NAN_LONG_VALUE))); + assertTrue(Double.POSITIVE_INFINITY == Double.longBitsToDouble(POSINF_LONG_VALUE)); + assertTrue(Double.NEGATIVE_INFINITY == Double.longBitsToDouble(NEGINF_LONG_VALUE)); + assertTrue(Double.MAX_VALUE == Double.longBitsToDouble(MAXD_LONG_VALUE)); + assertTrue(Double.MIN_VALUE == Double.longBitsToDouble(MIND_LONG_VALUE)); + assertTrue(Double.MIN_NORMAL == Double.longBitsToDouble(MINNORM_LONG_VALUE)); + assertTrue(TEST1_DOUBLE_VALUE == Double.longBitsToDouble(TEST1_LONG_VALUE)); + assertTrue(-TEST1_DOUBLE_VALUE == Double.longBitsToDouble(NEGTEST1_LONG_VALUE)); + } + public void testParse() { assertTrue(0 == Double.parseDouble("0")); assertTrue(-1.5 == Double.parseDouble("-1.5"));