Refactors longBitsToDouble, doubleBitsToLong
... and their int/float friends to use typed arrays which is a much
more compact implementation.
CL also attempted introduce {float,double}ToRaw{Long,Int}Bits
however I kept them private since Firefox doesn't work correctly
(seems like it always returns canonical form of NaN).
PiperOrigin-RevId: 355998261
Change-Id: Id4091202944d77d84476bfbd293b8831f44311b0
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index 308973d..239a2f9 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -39,13 +39,13 @@
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import com.google.gwt.thirdparty.guava.common.io.Files;
import com.google.gwt.util.tools.Utility;
-
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.regex.Pattern;
/**
@@ -2746,32 +2746,37 @@
// List of JRE types that provide JsInterop entry points and jre native JsTypes. These are
// always traversed fully and polute the tests, so they will be removed from stale type
// comparisons.
- staleTypeNames.removeAll(Arrays.asList(
- "java.io.HasSerializableTypeMarker",
- "java.io.Serializable",
- "java.lang.Boolean",
- "java.lang.CharSequence",
- "java.lang.Cloneable",
- "java.lang.Comparable",
- "java.lang.Double",
- "java.lang.HasCharSequenceTypeMarker",
- "java.lang.HasCloneableTypeMarker",
- "java.lang.HasComparableTypeMarker",
- "java.lang.Integer$NativeNumber",
- "java.lang.Number",
- "java.lang.Number$JavaLangNumber",
- "java.lang.String",
- "java.lang.String$NativeFunction",
- "java.lang.String$NativeString",
- "java.lang.Throwable",
- "java.lang.Throwable$HasJavaThrowable",
- "java.lang.Throwable$NativeError",
- "java.lang.Throwable$NativeTypeError",
- "javaemul.internal.JsUtils",
- "javaemul.internal.JsUtils$NativeNumber",
- "javaemul.internal.HashCodes",
- "javaemul.internal.NativeRegExp",
- "javaemul.internal.NativeRegExp$Match"));
+ staleTypeNames.removeAll(
+ Arrays.asList(
+ "java.io.HasSerializableTypeMarker",
+ "java.io.Serializable",
+ "java.lang.Boolean",
+ "java.lang.CharSequence",
+ "java.lang.Cloneable",
+ "java.lang.Comparable",
+ "java.lang.Double",
+ "java.lang.HasCharSequenceTypeMarker",
+ "java.lang.HasCloneableTypeMarker",
+ "java.lang.HasComparableTypeMarker",
+ "java.lang.Integer$NativeNumber",
+ "java.lang.Number",
+ "java.lang.Number$JavaLangNumber",
+ "java.lang.String",
+ "java.lang.String$NativeFunction",
+ "java.lang.String$NativeString",
+ "java.lang.Throwable",
+ "java.lang.Throwable$HasJavaThrowable",
+ "java.lang.Throwable$NativeError",
+ "java.lang.Throwable$NativeTypeError"));
+
+ staleTypeNames.removeIf(
+ new Predicate<String>() {
+ @Override
+ public boolean test(String name) {
+ return name.startsWith("javaemul.internal");
+ }
+ });
+
return staleTypeNames;
}
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 550c31a..8efc156 100644
--- a/user/super/com/google/gwt/emul/java/lang/Double.java
+++ b/user/super/com/google/gwt/emul/java/lang/Double.java
@@ -39,62 +39,6 @@
public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Double> TYPE = double.class;
- // 2^512, 2^-512
- private static final double POWER_512 = 1.3407807929942597E154;
- private static final double POWER_MINUS_512 = 7.458340731200207E-155;
- // 2^256, 2^-256
- private static final double POWER_256 = 1.157920892373162E77;
- private static final double POWER_MINUS_256 = 8.636168555094445E-78;
- // 2^128, 2^-128
- private static final double POWER_128 = 3.4028236692093846E38;
- private static final double POWER_MINUS_128 = 2.9387358770557188E-39;
- // 2^64, 2^-64
- private static final double POWER_64 = 18446744073709551616.0;
- private static final double POWER_MINUS_64 = 5.421010862427522E-20;
- // 2^52, 2^-52
- private static final double POWER_52 = 4503599627370496.0;
- private static final double POWER_MINUS_52 = 2.220446049250313E-16;
- // 2^32, 2^-32
- private static final double POWER_32 = 4294967296.0;
- private static final double POWER_MINUS_32 = 2.3283064365386963E-10;
- // 2^31
- private static final double POWER_31 = 2147483648.0;
- // 2^20, 2^-20
- private static final double POWER_20 = 1048576.0;
- private static final double POWER_MINUS_20 = 9.5367431640625E-7;
- // 2^16, 2^-16
- private static final double POWER_16 = 65536.0;
- private static final double POWER_MINUS_16 = 0.0000152587890625;
- // 2^8, 2^-8
- private static final double POWER_8 = 256.0;
- private static final double POWER_MINUS_8 = 0.00390625;
- // 2^4, 2^-4
- private static final double POWER_4 = 16.0;
- private static final double POWER_MINUS_4 = 0.0625;
- // 2^2, 2^-2
- private static final double POWER_2 = 4.0;
- private static final double POWER_MINUS_2 = 0.25;
- // 2^1, 2^-1
- private static final double POWER_1 = 2.0;
- private static final double POWER_MINUS_1 = 0.5;
- // 2^-1022 (smallest double non-denorm)
- private static final double POWER_MINUS_1022 = 2.2250738585072014E-308;
-
-
-
- private static class PowersTable {
- private static final double[] powers = {
- POWER_512, POWER_256, POWER_128, POWER_64, POWER_32, POWER_16, POWER_8,
- POWER_4, POWER_2, POWER_1
- };
-
- private static final double[] invPowers = {
- POWER_MINUS_512, POWER_MINUS_256, POWER_MINUS_128, POWER_MINUS_64,
- POWER_MINUS_32, POWER_MINUS_16, POWER_MINUS_8, POWER_MINUS_4, POWER_MINUS_2,
- POWER_MINUS_1
- };
- }
-
public static int compare(double x, double y) {
if (x < y) {
return -1;
@@ -118,82 +62,17 @@
}
public static long doubleToLongBits(double value) {
+ // Return a canonical NaN
if (isNaN(value)) {
return 0x7ff8000000000000L;
}
- boolean negative = false;
- if (value == 0.0) {
- if (1.0 / value == NEGATIVE_INFINITY) {
- return 0x8000000000000000L; // -0.0
- } else {
- return 0x0L;
- }
- }
- if (value < 0.0) {
- negative = true;
- value = -value;
- }
- if (isInfinite(value)) {
- if (negative) {
- return 0xfff0000000000000L;
- } else {
- return 0x7ff0000000000000L;
- }
- }
+ return doubleToRawLongBits(value);
+ }
- int exp = 0;
-
- // Scale d by powers of 2 into the range [1.0, 2.0)
- // If the exponent would go below -1023, scale into (0.0, 1.0) instead
- if (value < 1.0) {
- int bit = 512;
- for (int i = 0; i < 10; i++, bit >>= 1) {
- if (value < PowersTable.invPowers[i] && exp - bit >= -1023) {
- value *= PowersTable.powers[i];
- exp -= bit;
- }
- }
- // Force into [1.0, 2.0) range
- if (value < 1.0 && exp - 1 >= -1023) {
- value *= 2.0;
- exp--;
- }
- } else if (value >= 2.0) {
- int bit = 512;
- for (int i = 0; i < 10; i++, bit >>= 1) {
- if (value >= PowersTable.powers[i]) {
- value *= PowersTable.invPowers[i];
- exp += bit;
- }
- }
- }
-
- if (exp > -1023) {
- // Remove significand of non-denormalized mantissa
- value -= 1.0;
- } else {
- // Insert 0 bit as significand of denormalized mantissa
- value *= 0.5;
- }
-
- // Extract high 20 bits of mantissa
- long ihi = (long) (value * POWER_20);
-
- // Extract low 32 bits of mantissa
- value -= ihi * POWER_MINUS_20;
-
- long ilo = (long) (value * POWER_52);
-
- // Exponent bits
- ihi |= (exp + 1023) << 20;
-
- // Sign bit
- if (negative) {
- ihi |= 0x80000000L;
- }
-
- return (ihi << 32) | ilo;
+ // This method is kept private since it returns canonical NaN in Firefox.
+ private static long doubleToRawLongBits(double value) {
+ return JsUtils.doubleToRawLongBits(value);
}
public static int hashCode(double d) {
@@ -211,55 +90,7 @@
public static native boolean isNaN(double x);
public static double longBitsToDouble(long bits) {
- long ihi = (long) (bits >> 32);
- long ilo = (long) (bits & 0xffffffffL);
- if (ihi < 0) {
- ihi += 0x100000000L;
- }
- if (ilo < 0) {
- ilo += 0x100000000L;
- }
-
- boolean negative = (ihi & 0x80000000) != 0;
- int exp = (int) ((ihi >> 20) & 0x7ff);
- ihi &= 0xfffff; // remove sign bit and exponent
-
- if (exp == 0x0) {
- double d = (ihi * POWER_MINUS_20) + (ilo * POWER_MINUS_52);
- d *= POWER_MINUS_1022;
- return negative ? (d == 0.0 ? -0.0 : -d) : d;
- } else if (exp == 0x7ff) {
- if (ihi == 0 && ilo == 0) {
- return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- } else {
- return Double.NaN;
- }
- }
-
- // Normalize exponent
- exp -= 1023;
-
- double d = 1.0 + (ihi * POWER_MINUS_20) + (ilo * POWER_MINUS_52);
- if (exp > 0) {
- int bit = 512;
- for (int i = 0; i < 10; i++, bit >>= 1) {
- if (exp >= bit) {
- d *= PowersTable.powers[i];
- exp -= bit;
- }
- }
- } else if (exp < 0) {
- while (exp < 0) {
- int bit = 512;
- for (int i = 0; i < 10; i++, bit >>= 1) {
- if (exp <= -bit) {
- d *= PowersTable.invPowers[i];
- exp += bit;
- }
- }
- }
- }
- return negative ? -d : d;
+ return JsUtils.longBitsToDouble(bits);
}
public static double max(double a, double b) {
diff --git a/user/super/com/google/gwt/emul/java/lang/Float.java b/user/super/com/google/gwt/emul/java/lang/Float.java
index 64340d4..4e5ec5e 100644
--- a/user/super/com/google/gwt/emul/java/lang/Float.java
+++ b/user/super/com/google/gwt/emul/java/lang/Float.java
@@ -15,6 +15,8 @@
*/
package java.lang;
+import javaemul.internal.JsUtils;
+
/**
* Wraps a primitive <code>float</code> as an object.
*/
@@ -43,46 +45,12 @@
return 0x7fc00000;
}
- if (value == 0.0f) {
- if (1.0 / value == NEGATIVE_INFINITY) {
- return 0x80000000; // -0.0f
- } else {
- return 0x0;
- }
- }
- boolean negative = false;
- if (value < 0.0) {
- negative = true;
- value = -value;
- }
- if (isInfinite(value)) {
- if (negative) {
- return 0xff800000;
- } else {
- return 0x7f800000;
- }
- }
+ return floatToRawIntBits(value);
+ }
- // Obtain the 64-bit representation and extract its exponent and
- // mantissa.
- long l = Double.doubleToLongBits((double) value);
- int exp = (int) (((l >> 52) & 0x7ff) - 1023);
- int mantissa = (int) ((l & 0xfffffffffffffL) >> 29);
-
- // If the number will be a denorm in the float representation
- // (i.e., its exponent is -127 or smaller), add a leading 1 to the
- // mantissa and shift it right to maintain an exponent of -127.
- if (exp <= -127) {
- mantissa = (0x800000 | mantissa) >> (-127 - exp + 1);
- exp = -127;
- }
-
- // Construct the 32-bit representation
- long bits = negative ? POWER_31_INT : 0x0L;
- bits |= (exp + 127) << 23;
- bits |= mantissa;
-
- return (int) bits;
+ // This method is kept private since it returns canonical NaN in Firefox.
+ private static int floatToRawIntBits(float value) {
+ return JsUtils.floatToRawIntBits(value);
}
/**
@@ -94,40 +62,7 @@
}
public static float intBitsToFloat(int bits) {
- boolean negative = (bits & 0x80000000) != 0;
- int exp = (bits >> 23) & 0xff;
- bits &= 0x7fffff;
-
- if (exp == 0x0) {
- // Handle +/- 0 here, denorms below
- if (bits == 0) {
- return negative ? -0.0f : 0.0f;
- }
- } else if (exp == 0xff) {
- // Inf & NaN
- if (bits == 0) {
- return negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
- } else {
- return NaN;
- }
- }
-
- if (exp == 0) {
- // Input is denormalized, renormalize by shifting left until there is a
- // leading 1
- exp = 1;
- while ((bits & 0x800000) == 0) {
- bits <<= 1;
- exp--;
- }
- bits &= 0x7fffff;
- }
-
- // Build the bits of a 64-bit double from the incoming bits
- long bits64 = negative ? 0x8000000000000000L : 0x0L;
- bits64 |= ((long) (exp + 896)) << 52;
- bits64 |= ((long) bits) << 29;
- return (float) Double.longBitsToDouble(bits64);
+ return JsUtils.intBitsToFloat(bits);
}
public static boolean isFinite(float x) {
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
index c8bf1e8..21bf263 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
@@ -87,5 +87,56 @@
public static native <T> T getProperty(Object map, String key) /*-{
return map[key];
}-*/;
+
+ @JsType(isNative = true, namespace = "<window>")
+ static class ArrayBuffer {
+ ArrayBuffer(int size) {}
+ }
+
+ @JsType(isNative = true, namespace = "<window>")
+ static class Float64Array {
+ Float64Array(ArrayBuffer buf) {}
+ }
+
+ @JsType(isNative = true, namespace = "<window>")
+ static class Float32Array {
+ Float32Array(ArrayBuffer buf) {}
+ }
+
+ @JsType(isNative = true, namespace = "<window>")
+ static class Uint32Array {
+ Uint32Array(ArrayBuffer buf) {}
+ }
+
+ public static int floatToRawIntBits(float value) {
+ ArrayBuffer buf = new ArrayBuffer(4);
+ JsUtils.<float[]>uncheckedCast(new Float32Array(buf))[0] = value;
+ return JsUtils.<int[]>uncheckedCast(new Uint32Array(buf))[0] | 0;
+ }
+
+ public static float intBitsToFloat(int value) {
+ ArrayBuffer buf = new ArrayBuffer(4);
+ JsUtils.<int[]>uncheckedCast(new Uint32Array(buf))[0] = value;
+ return JsUtils.<float[]>uncheckedCast(new Float32Array(buf))[0];
+ }
+
+ public static long doubleToRawLongBits(double value) {
+ ArrayBuffer buf = new ArrayBuffer(8);
+ JsUtils.<double[]>uncheckedCast(new Float64Array(buf))[0] = value;
+ int[] intBits = JsUtils.<int[]>uncheckedCast(new Uint32Array(buf));
+ long highBits = ((long) (intBits[1] | 0)) << 32;
+ long lowBits = (intBits[0] | 0) & 0x00000000ffffffffL;
+ return highBits | lowBits;
+ }
+
+ public static double longBitsToDouble(long value) {
+ ArrayBuffer buf = new ArrayBuffer(8);
+ int[] intBits = JsUtils.<int[]>uncheckedCast(new Uint32Array(buf));
+ intBits[0] = (int) value;
+ intBits[1] = (int) (value >>> 32);
+ return JsUtils.<double[]>uncheckedCast(new Float64Array(buf))[0];
+ }
+
+ private JsUtils() {}
}
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 8567199..befde65 100644
--- a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
@@ -539,5 +539,12 @@
// Do we need a special check for +inf == +inf and -inf == -inf?
assertEquals(expected, actual);
}
-}
+ // Disabled since it is broken for Firefox.
+ // public void testDoubleToRawLongBits() {
+ // long l = 0x7ff80000000004d2L;
+ // double d = Double.longBitsToDouble(l);
+ // assertEquals(l, Double.doubleToRawLongBits(d));
+ // assertEquals(0x7ff8000000000000L, Double.doubleToLongBits(d));
+ // }
+}
diff --git a/user/test/com/google/gwt/emultest/java/lang/FloatTest.java b/user/test/com/google/gwt/emultest/java/lang/FloatTest.java
index 395bcd1..be7284c 100644
--- a/user/test/com/google/gwt/emultest/java/lang/FloatTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/FloatTest.java
@@ -426,4 +426,12 @@
}
assertEquals(expected, actual);
}
+
+ // Disabled since it is broken for Firefox.
+ // public void testFloatToRawIntBits() {
+ // int i = 0x7fc004d2;
+ // float f = Float.intBitsToFloat(i);
+ // assertEquals(i, Float.floatToRawIntBits(f));
+ // assertEquals(0x7fc00000, Float.floatToIntBits(f));
+ // }
}