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));
+  // }
 }