Bug 939843: Unify FloatingPoint's code for Double and Float; r=waldo
authorBenjamin Bouvier <benj@benj.me>
Thu, 30 Jan 2014 15:54:46 +0100
changeset 188223 74196538e7a21c2bfdb370b392f4425d5db3d713
parent 188222 2c0ffb315be38cfbe05fb697f0877c07c3be24c9
child 188224 5de01e2586e98869bb2b589baf7edf5cf7c23adb
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs939843
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 939843: Unify FloatingPoint's code for Double and Float; r=waldo
mfbt/FloatingPoint.h
mfbt/decimal/moz-decimal-utils.h
mfbt/tests/TestFloatingPoint.cpp
--- a/mfbt/FloatingPoint.h
+++ b/mfbt/FloatingPoint.h
@@ -29,271 +29,308 @@ namespace mozilla {
  * algorithms!  But with some care we've found algorithms that seem to not
  * trigger those compiler bugs.
  *
  * For the aforementioned reasons, be very wary of making changes to any of
  * these algorithms.  If you must make changes, keep a careful eye out for
  * compiler bustage, particularly PGO-specific bustage.
  */
 
-/*
- * These implementations all assume |double| is a 64-bit double format number
- * type, compatible with the IEEE-754 standard.  C/C++ don't require this to be
- * the case.  But we required this in implementations of these algorithms that
- * preceded this header, so we shouldn't break anything if we continue doing so.
- */
-static_assert(sizeof(double) == sizeof(uint64_t), "double must be 64 bits");
+struct FloatTypeTraits
+{
+    typedef uint32_t Bits;
 
-const unsigned DoubleExponentBias = 1023;
-const unsigned DoubleExponentShift = 52;
+    static const unsigned ExponentBias = 127;
+    static const unsigned ExponentShift = 23;
+
+    static const Bits SignBit         = 0x80000000UL;
+    static const Bits ExponentBits    = 0x7F800000UL;
+    static const Bits SignificandBits = 0x007FFFFFUL;
+};
 
-const uint64_t DoubleSignBit         = 0x8000000000000000ULL;
-const uint64_t DoubleExponentBits    = 0x7ff0000000000000ULL;
-const uint64_t DoubleSignificandBits = 0x000fffffffffffffULL;
+struct DoubleTypeTraits
+{
+    typedef uint64_t Bits;
+
+    static const unsigned ExponentBias = 1023;
+    static const unsigned ExponentShift = 52;
 
-static_assert((DoubleSignBit & DoubleExponentBits) == 0,
-              "sign bit doesn't overlap exponent bits");
-static_assert((DoubleSignBit & DoubleSignificandBits) == 0,
-              "sign bit doesn't overlap significand bits");
-static_assert((DoubleExponentBits & DoubleSignificandBits) == 0,
-              "exponent bits don't overlap significand bits");
+    static const Bits SignBit         = 0x8000000000000000ULL;
+    static const Bits ExponentBits    = 0x7ff0000000000000ULL;
+    static const Bits SignificandBits = 0x000fffffffffffffULL;
+};
 
-static_assert((DoubleSignBit | DoubleExponentBits | DoubleSignificandBits) ==
-              ~uint64_t(0),
-              "all bits accounted for");
+template<typename T> struct SelectTrait;
+template<> struct SelectTrait<float> : public FloatTypeTraits {};
+template<> struct SelectTrait<double> : public DoubleTypeTraits {};
 
 /*
- * Ditto for |float| that must be a 32-bit double format number type, compatible
- * with the IEEE-754 standard.
+ *  This struct contains details regarding the encoding of floating-point
+ *  numbers that can be useful for direct bit manipulation. As of now, the
+ *  template parameter has to be float or double.
+ *
+ *  The nested typedef |Bits| is the unsigned integral type with the same size
+ *  as T: uint32_t for float and uint64_t for double (static assertions
+ *  double-check these assumptions).
+ *
+ *  ExponentBias is the offset that is subtracted from the exponent when
+ *  computing the value, i.e. one plus the opposite of the mininum possible
+ *  exponent.
+ *  ExponentShift is the shift that one needs to apply to retrieve the exponent
+ *  component of the value.
+ *
+ *  SignBit contains a bits mask. Bit-and-ing with this mask will result in
+ *  obtaining the sign bit.
+ *  ExponentBits contains the mask needed for obtaining the exponent bits and
+ *  SignificandBits contains the mask needed for obtaining the significand bits.
+ *
+ *  Full details of how floating point number formats are encoded are beyond the
+ *  scope of this comment. For more information, see
+ *  http://en.wikipedia.org/wiki/IEEE_floating_point
+ *  http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers
  */
-static_assert(sizeof(float) == sizeof(uint32_t), "float must be 32bits");
-
-const unsigned FloatExponentBias = 127;
-const unsigned FloatExponentShift = 23;
+template<typename T>
+struct FloatingPoint : public SelectTrait<T>
+{
+    typedef SelectTrait<T> Base;
+    typedef typename Base::Bits Bits;
 
-const uint32_t FloatSignBit         = 0x80000000UL;
-const uint32_t FloatExponentBits    = 0x7F800000UL;
-const uint32_t FloatSignificandBits = 0x007FFFFFUL;
+    static_assert((Base::SignBit & Base::ExponentBits) == 0,
+                  "sign bit shouldn't overlap exponent bits");
+    static_assert((Base::SignBit & Base::SignificandBits) == 0,
+                  "sign bit shouldn't overlap significand bits");
+    static_assert((Base::ExponentBits & Base::SignificandBits) == 0,
+                  "exponent bits shouldn't overlap significand bits");
 
-static_assert((FloatSignBit & FloatExponentBits) == 0,
-              "sign bit doesn't overlap exponent bits");
-static_assert((FloatSignBit & FloatSignificandBits) == 0,
-              "sign bit doesn't overlap significand bits");
-static_assert((FloatExponentBits & FloatSignificandBits) == 0,
-              "exponent bits don't overlap significand bits");
+    static_assert((Base::SignBit | Base::ExponentBits | Base::SignificandBits) ==
+                  ~Bits(0),
+                  "all bits accounted for");
 
-static_assert((FloatSignBit | FloatExponentBits | FloatSignificandBits) ==
-              ~uint32_t(0),
-              "all bits accounted for");
+    /*
+     * These implementations assume float/double are 32/64-bit single/double format
+     * number types compatible with the IEEE-754 standard.  C++ don't require this
+     * to be the case.  But we required this in implementations of these algorithms
+     * that preceded this header, so we shouldn't break anything if we keep doing so.
+     */
+    static_assert(sizeof(T) == sizeof(Bits), "Bits must be same size as T");
+};
 
 /** Determines whether a double is NaN. */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-IsNaN(double d)
+IsNaN(T t)
 {
   /*
-   * A double is NaN if all exponent bits are 1 and the significand contains at
+   * A float/double is NaN if all exponent bits are 1 and the significand contains at
    * least one non-zero bit.
    */
-  uint64_t bits = BitwiseCast<uint64_t>(d);
-  return (bits & DoubleExponentBits) == DoubleExponentBits &&
-         (bits & DoubleSignificandBits) != 0;
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  Bits bits = BitwiseCast<Bits>(t);
+  return (bits & Traits::ExponentBits) == Traits::ExponentBits &&
+         (bits & Traits::SignificandBits) != 0;
 }
 
-/** Determines whether a double is +Infinity or -Infinity. */
+/** Determines whether a float/double is +Infinity or -Infinity. */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-IsInfinite(double d)
+IsInfinite(T t)
 {
   /* Infinities have all exponent bits set to 1 and an all-0 significand. */
-  uint64_t bits = BitwiseCast<uint64_t>(d);
-  return (bits & ~DoubleSignBit) == DoubleExponentBits;
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  Bits bits = BitwiseCast<Bits>(t);
+  return (bits & ~Traits::SignBit) == Traits::ExponentBits;
 }
 
-/** Determines whether a double is not NaN or infinite. */
+/** Determines whether a float/double is not NaN or infinite. */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-IsFinite(double d)
+IsFinite(T t)
 {
   /*
-   * NaN and Infinities are the only non-finite doubles, and both have all
+   * NaN and Infinities are the only non-finite floats/doubles, and both have all
    * exponent bits set to 1.
    */
-  uint64_t bits = BitwiseCast<uint64_t>(d);
-  return (bits & DoubleExponentBits) != DoubleExponentBits;
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  Bits bits = BitwiseCast<Bits>(t);
+  return (bits & Traits::ExponentBits) != Traits::ExponentBits;
 }
 
 /**
- * Determines whether a double is negative.  It is an error to call this method
- * on a double which is NaN.
+ * Determines whether a float/double is negative.  It is an error to call this method
+ * on a float/double which is NaN.
  */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-IsNegative(double d)
+IsNegative(T t)
 {
-  MOZ_ASSERT(!IsNaN(d), "NaN does not have a sign");
+  MOZ_ASSERT(!IsNaN(t), "NaN does not have a sign");
 
   /* The sign bit is set if the double is negative. */
-  uint64_t bits = BitwiseCast<uint64_t>(d);
-  return (bits & DoubleSignBit) != 0;
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  Bits bits = BitwiseCast<Bits>(t);
+  return (bits & Traits::SignBit) != 0;
 }
 
-/** Determines whether a double represents -0. */
+/** Determines whether a float/double represents -0. */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-IsNegativeZero(double d)
+IsNegativeZero(T t)
 {
-  /* Only the sign bit is set if the double is -0. */
-  uint64_t bits = BitwiseCast<uint64_t>(d);
-  return bits == DoubleSignBit;
+  /* Only the sign bit is set if the value is -0. */
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  Bits bits = BitwiseCast<Bits>(t);
+  return bits == Traits::SignBit;
 }
 
 /**
- * Returns the exponent portion of the double.
+ * Returns the exponent portion of the float/double.
  *
  * Zero is not special-cased, so ExponentComponent(0.0) is
- * -int_fast16_t(DoubleExponentBias).
+ * -int_fast16_t(Traits::ExponentBias).
  */
+template<typename T>
 static MOZ_ALWAYS_INLINE int_fast16_t
-ExponentComponent(double d)
+ExponentComponent(T t)
 {
   /*
-   * The exponent component of a double is an unsigned number, biased from its
+   * The exponent component of a float/double is an unsigned number, biased from its
    * actual value.  Subtract the bias to retrieve the actual exponent.
    */
-  uint64_t bits = BitwiseCast<uint64_t>(d);
-  return int_fast16_t((bits & DoubleExponentBits) >> DoubleExponentShift) -
-         int_fast16_t(DoubleExponentBias);
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  Bits bits = BitwiseCast<Bits>(t);
+  return int_fast16_t((bits & Traits::ExponentBits) >> Traits::ExponentShift) -
+         int_fast16_t(Traits::ExponentBias);
 }
 
 /** Returns +Infinity. */
-static MOZ_ALWAYS_INLINE double
+template<typename T>
+static MOZ_ALWAYS_INLINE T
 PositiveInfinity()
 {
   /*
    * Positive infinity has all exponent bits set, sign bit set to 0, and no
    * significand.
    */
-  return BitwiseCast<double>(DoubleExponentBits);
+  typedef FloatingPoint<T> Traits;
+  return BitwiseCast<T>(Traits::ExponentBits);
 }
 
 /** Returns -Infinity. */
-static MOZ_ALWAYS_INLINE double
+template<typename T>
+static MOZ_ALWAYS_INLINE T
 NegativeInfinity()
 {
   /*
    * Negative infinity has all exponent bits set, sign bit set to 1, and no
    * significand.
    */
-  return BitwiseCast<double>(DoubleSignBit | DoubleExponentBits);
+  typedef FloatingPoint<T> Traits;
+  return BitwiseCast<T>(Traits::SignBit | Traits::ExponentBits);
 }
 
+
 /** Constructs a NaN value with the specified sign bit and significand bits. */
-static MOZ_ALWAYS_INLINE double
-SpecificNaN(int signbit, uint64_t significand)
+template<typename T>
+static MOZ_ALWAYS_INLINE T
+SpecificNaN(int signbit, typename FloatingPoint<T>::Bits significand)
 {
+  typedef FloatingPoint<T> Traits;
   MOZ_ASSERT(signbit == 0 || signbit == 1);
-  MOZ_ASSERT((significand & ~DoubleSignificandBits) == 0);
-  MOZ_ASSERT(significand & DoubleSignificandBits);
+  MOZ_ASSERT((significand & ~Traits::SignificandBits) == 0);
+  MOZ_ASSERT(significand & Traits::SignificandBits);
 
-  double d = BitwiseCast<double>((signbit ? DoubleSignBit : 0) |
-                                 DoubleExponentBits |
-                                 significand);
-  MOZ_ASSERT(IsNaN(d));
-  return d;
+  T t = BitwiseCast<T>((signbit ? Traits::SignBit : 0) |
+                       Traits::ExponentBits |
+                       significand);
+  MOZ_ASSERT(IsNaN(t));
+  return t;
 }
 
-/** Computes the smallest non-zero positive double value. */
-static MOZ_ALWAYS_INLINE double
-MinDoubleValue()
+/** Computes the smallest non-zero positive float/double value. */
+template<typename T>
+static MOZ_ALWAYS_INLINE T
+MinNumberValue()
 {
-  return BitwiseCast<double>(uint64_t(1));
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  return BitwiseCast<T>(Bits(1));
 }
 
 /**
- * If d is equal to some int32_t value, set *i to that value and return true;
+ * If t is equal to some int32_t value, set *i to that value and return true;
  * otherwise return false.
  *
  * Note that negative zero is "equal" to zero here. To test whether a value can
- * be losslessly converted to int32_t and back, use DoubleIsInt32 instead.
+ * be losslessly converted to int32_t and back, use NumberIsInt32 instead.
  */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-DoubleEqualsInt32(double d, int32_t* i)
+NumberEqualsInt32(T t, int32_t* i)
 {
   /*
-   * XXX Casting a double that doesn't truncate to int32_t, to int32_t, induces
-   *     undefined behavior.  We should definitely fix this (bug 744965), but as
-   *     apparently it "works" in practice, it's not a pressing concern now.
+   * XXX Casting a floating-point value that doesn't truncate to int32_t, to
+   *     int32_t, induces undefined behavior.  We should definitely fix this
+   *     (bug 744965), but as apparently it "works" in practice, it's not a
+   *     pressing concern now.
    */
-  return d == (*i = int32_t(d));
+  return t == (*i = int32_t(t));
 }
 
 /**
  * If d can be converted to int32_t and back to an identical double value,
  * set *i to that value and return true; otherwise return false.
  *
- * The difference between this and DoubleEqualsInt32 is that this method returns
+ * The difference between this and NumberEqualsInt32 is that this method returns
  * false for negative zero.
  */
+template<typename T>
 static MOZ_ALWAYS_INLINE bool
-DoubleIsInt32(double d, int32_t* i)
+NumberIsInt32(T t, int32_t* i)
 {
-  return !IsNegativeZero(d) && DoubleEqualsInt32(d, i);
+  return !IsNegativeZero(t) && NumberEqualsInt32(t, i);
 }
 
 /**
  * Computes a NaN value.  Do not use this method if you depend upon a particular
  * NaN value being returned.
  */
-static MOZ_ALWAYS_INLINE double
+template<typename T>
+static MOZ_ALWAYS_INLINE T
 UnspecifiedNaN()
 {
   /*
    * If we can use any quiet NaN, we might as well use the all-ones NaN,
    * since it's cheap to materialize on common platforms (such as x64, where
    * this value can be represented in a 32-bit signed immediate field, allowing
    * it to be stored to memory in a single instruction).
    */
-  return SpecificNaN(1, 0xfffffffffffffULL);
+  typedef FloatingPoint<T> Traits;
+  return SpecificNaN<T>(1, Traits::SignificandBits);
 }
 
 /**
  * Compare two doubles for equality, *without* equating -0 to +0, and equating
  * any NaN value to any other NaN value.  (The normal equality operators equate
  * -0 with +0, and they equate NaN to no other value.)
  */
+template<typename T>
 static inline bool
-DoublesAreIdentical(double d1, double d2)
-{
-  if (IsNaN(d1))
-    return IsNaN(d2);
-  return BitwiseCast<uint64_t>(d1) == BitwiseCast<uint64_t>(d2);
-}
-
-/** Determines whether a float is NaN. */
-static MOZ_ALWAYS_INLINE bool
-IsFloatNaN(float f)
+NumbersAreIdentical(T t1, T t2)
 {
-  /*
-   * A float is NaN if all exponent bits are 1 and the significand contains at
-   * least one non-zero bit.
-   */
-  uint32_t bits = BitwiseCast<uint32_t>(f);
-  return (bits & FloatExponentBits) == FloatExponentBits &&
-         (bits & FloatSignificandBits) != 0;
-}
-
-/** Constructs a NaN value with the specified sign bit and significand bits. */
-static MOZ_ALWAYS_INLINE float
-SpecificFloatNaN(int signbit, uint32_t significand)
-{
-  MOZ_ASSERT(signbit == 0 || signbit == 1);
-  MOZ_ASSERT((significand & ~FloatSignificandBits) == 0);
-  MOZ_ASSERT(significand & FloatSignificandBits);
-
-  float f = BitwiseCast<float>((signbit ? FloatSignBit : 0) |
-                                 FloatExponentBits |
-                                 significand);
-  MOZ_ASSERT(IsFloatNaN(f));
-  return f;
+  typedef FloatingPoint<T> Traits;
+  typedef typename Traits::Bits Bits;
+  if (IsNaN(t1))
+    return IsNaN(t2);
+  return BitwiseCast<Bits>(t1) == BitwiseCast<Bits>(t2);
 }
 
 namespace detail {
 
 template<typename T>
 struct FuzzyEqualsEpsilon;
 
 template<>
--- a/mfbt/decimal/moz-decimal-utils.h
+++ b/mfbt/decimal/moz-decimal-utils.h
@@ -47,17 +47,17 @@ namespace std {
 }
 #endif
 
 typedef std::string String;
 
 double mozToDouble(const String &aStr, bool *valid) {
   double_conversion::StringToDoubleConverter converter(
     double_conversion::StringToDoubleConverter::NO_FLAGS,
-    mozilla::UnspecifiedNaN(), mozilla::UnspecifiedNaN(), nullptr, nullptr);
+    mozilla::UnspecifiedNaN<double>(), mozilla::UnspecifiedNaN<double>(), nullptr, nullptr);
   const char* str = aStr.c_str();
   int length = mozilla::SafeCast<int>(strlen(str));
   int processed_char_count; // unused - NO_FLAGS requires the whole string to parse
   double result = converter.StringToDouble(str, length, &processed_char_count);
   *valid = mozilla::IsFinite(result);
   return result;
 }
 
--- a/mfbt/tests/TestFloatingPoint.cpp
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -2,46 +2,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/FloatingPoint.h"
 
 #include <math.h>
 
-using mozilla::DoublesAreIdentical;
-using mozilla::DoubleExponentBias;
-using mozilla::DoubleEqualsInt32;
-using mozilla::DoubleIsInt32;
 using mozilla::ExponentComponent;
+using mozilla::FloatingPoint;
 using mozilla::FuzzyEqualsAdditive;
 using mozilla::FuzzyEqualsMultiplicative;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::NegativeInfinity;
+using mozilla::NumberEqualsInt32;
+using mozilla::NumberIsInt32;
+using mozilla::NumbersAreIdentical;
 using mozilla::PositiveInfinity;
-using mozilla::SpecificFloatNaN;
 using mozilla::SpecificNaN;
 using mozilla::UnspecifiedNaN;
 
 static void
 ShouldBeIdentical(double d1, double d2)
 {
-  MOZ_ASSERT(DoublesAreIdentical(d1, d2));
-  MOZ_ASSERT(DoublesAreIdentical(d2, d1));
+  MOZ_ASSERT(NumbersAreIdentical(d1, d2));
+  MOZ_ASSERT(NumbersAreIdentical(d2, d1));
 }
 
 static void
 ShouldNotBeIdentical(double d1, double d2)
 {
-  MOZ_ASSERT(!DoublesAreIdentical(d1, d2));
-  MOZ_ASSERT(!DoublesAreIdentical(d2, d1));
+  MOZ_ASSERT(!NumbersAreIdentical(d1, d2));
+  MOZ_ASSERT(!NumbersAreIdentical(d2, d1));
 }
 
 static void
 TestDoublesAreIdentical()
 {
   ShouldBeIdentical(+0.0, +0.0);
   ShouldBeIdentical(-0.0, -0.0);
   ShouldNotBeIdentical(+0.0, -0.0);
@@ -49,153 +48,153 @@ TestDoublesAreIdentical()
   ShouldBeIdentical(1.0, 1.0);
   ShouldNotBeIdentical(-1.0, 1.0);
   ShouldBeIdentical(4294967295.0, 4294967295.0);
   ShouldNotBeIdentical(-4294967295.0, 4294967295.0);
   ShouldBeIdentical(4294967296.0, 4294967296.0);
   ShouldBeIdentical(4294967297.0, 4294967297.0);
   ShouldBeIdentical(1e300, 1e300);
 
-  ShouldBeIdentical(PositiveInfinity(), PositiveInfinity());
-  ShouldBeIdentical(NegativeInfinity(), NegativeInfinity());
-  ShouldNotBeIdentical(PositiveInfinity(), NegativeInfinity());
+  ShouldBeIdentical(PositiveInfinity<double>(), PositiveInfinity<double>());
+  ShouldBeIdentical(NegativeInfinity<double>(), NegativeInfinity<double>());
+  ShouldNotBeIdentical(PositiveInfinity<double>(), NegativeInfinity<double>());
 
-  ShouldNotBeIdentical(-0.0, NegativeInfinity());
-  ShouldNotBeIdentical(+0.0, NegativeInfinity());
-  ShouldNotBeIdentical(1e300, NegativeInfinity());
-  ShouldNotBeIdentical(3.141592654, NegativeInfinity());
+  ShouldNotBeIdentical(-0.0, NegativeInfinity<double>());
+  ShouldNotBeIdentical(+0.0, NegativeInfinity<double>());
+  ShouldNotBeIdentical(1e300, NegativeInfinity<double>());
+  ShouldNotBeIdentical(3.141592654, NegativeInfinity<double>());
 
-  ShouldBeIdentical(UnspecifiedNaN(), UnspecifiedNaN());
-  ShouldBeIdentical(-UnspecifiedNaN(), UnspecifiedNaN());
-  ShouldBeIdentical(UnspecifiedNaN(), -UnspecifiedNaN());
+  ShouldBeIdentical(UnspecifiedNaN<double>(), UnspecifiedNaN<double>());
+  ShouldBeIdentical(-UnspecifiedNaN<double>(), UnspecifiedNaN<double>());
+  ShouldBeIdentical(UnspecifiedNaN<double>(), -UnspecifiedNaN<double>());
 
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 42));
-  ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(1, 42));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(1, 42));
-  ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 42));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 42));
+  ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(1, 42));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(1, 42));
+  ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(0, 42));
 
   const uint64_t Mask = 0xfffffffffffffULL;
   for (unsigned i = 0; i < 52; i++) {
     for (unsigned j = 0; j < 52; j++) {
       for (unsigned sign = 0; i < 2; i++) {
-        ShouldBeIdentical(SpecificNaN(0, 1ULL << i), SpecificNaN(sign, 1ULL << j));
-        ShouldBeIdentical(SpecificNaN(1, 1ULL << i), SpecificNaN(sign, 1ULL << j));
+        ShouldBeIdentical(SpecificNaN<double>(0, 1ULL << i), SpecificNaN<double>(sign, 1ULL << j));
+        ShouldBeIdentical(SpecificNaN<double>(1, 1ULL << i), SpecificNaN<double>(sign, 1ULL << j));
 
-        ShouldBeIdentical(SpecificNaN(0, Mask & ~(1ULL << i)),
-                          SpecificNaN(sign, Mask & ~(1ULL << j)));
-        ShouldBeIdentical(SpecificNaN(1, Mask & ~(1ULL << i)),
-                          SpecificNaN(sign, Mask & ~(1ULL << j)));
+        ShouldBeIdentical(SpecificNaN<double>(0, Mask & ~(1ULL << i)),
+                          SpecificNaN<double>(sign, Mask & ~(1ULL << j)));
+        ShouldBeIdentical(SpecificNaN<double>(1, Mask & ~(1ULL << i)),
+                          SpecificNaN<double>(sign, Mask & ~(1ULL << j)));
       }
     }
   }
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x8000000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x4000000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x2000000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x1000000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0800000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0400000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0200000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0100000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0080000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0040000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0020000000000ULL));
-  ShouldBeIdentical(SpecificNaN(0, 17), SpecificNaN(0, 0x0010000000000ULL));
-  ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 0xff0ffffffffffULL));
-  ShouldBeIdentical(SpecificNaN(1, 17), SpecificNaN(0, 0xfffffffffff0fULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x8000000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x4000000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x2000000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x1000000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0800000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0400000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0200000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0100000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0080000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0040000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0020000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 0x0010000000000ULL));
+  ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(0, 0xff0ffffffffffULL));
+  ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(0, 0xfffffffffff0fULL));
 
-  ShouldNotBeIdentical(UnspecifiedNaN(), +0.0);
-  ShouldNotBeIdentical(UnspecifiedNaN(), -0.0);
-  ShouldNotBeIdentical(UnspecifiedNaN(), 1.0);
-  ShouldNotBeIdentical(UnspecifiedNaN(), -1.0);
-  ShouldNotBeIdentical(UnspecifiedNaN(), PositiveInfinity());
-  ShouldNotBeIdentical(UnspecifiedNaN(), NegativeInfinity());
+  ShouldNotBeIdentical(UnspecifiedNaN<double>(), +0.0);
+  ShouldNotBeIdentical(UnspecifiedNaN<double>(), -0.0);
+  ShouldNotBeIdentical(UnspecifiedNaN<double>(), 1.0);
+  ShouldNotBeIdentical(UnspecifiedNaN<double>(), -1.0);
+  ShouldNotBeIdentical(UnspecifiedNaN<double>(), PositiveInfinity<double>());
+  ShouldNotBeIdentical(UnspecifiedNaN<double>(), NegativeInfinity<double>());
 }
 
 static void
 TestExponentComponent()
 {
-  MOZ_ASSERT(ExponentComponent(0.0) == -int_fast16_t(DoubleExponentBias));
-  MOZ_ASSERT(ExponentComponent(-0.0) == -int_fast16_t(DoubleExponentBias));
+  MOZ_ASSERT(ExponentComponent(0.0) == -int_fast16_t(FloatingPoint<double>::ExponentBias));
+  MOZ_ASSERT(ExponentComponent(-0.0) == -int_fast16_t(FloatingPoint<double>::ExponentBias));
   MOZ_ASSERT(ExponentComponent(0.125) == -3);
   MOZ_ASSERT(ExponentComponent(0.5) == -1);
   MOZ_ASSERT(ExponentComponent(1.0) == 0);
   MOZ_ASSERT(ExponentComponent(1.5) == 0);
   MOZ_ASSERT(ExponentComponent(2.0) == 1);
-  MOZ_ASSERT(ExponentComponent(7) == 2);
-  MOZ_ASSERT(ExponentComponent(PositiveInfinity()) == DoubleExponentBias + 1);
-  MOZ_ASSERT(ExponentComponent(NegativeInfinity()) == DoubleExponentBias + 1);
-  MOZ_ASSERT(ExponentComponent(UnspecifiedNaN()) == DoubleExponentBias + 1);
+  MOZ_ASSERT(ExponentComponent(7.0) == 2);
+  MOZ_ASSERT(ExponentComponent(PositiveInfinity<double>()) == FloatingPoint<double>::ExponentBias + 1);
+  MOZ_ASSERT(ExponentComponent(NegativeInfinity<double>()) == FloatingPoint<double>::ExponentBias + 1);
+  MOZ_ASSERT(ExponentComponent(UnspecifiedNaN<double>()) == FloatingPoint<double>::ExponentBias + 1);
 }
 
 static void
 TestPredicates()
 {
-  MOZ_ASSERT(IsNaN(UnspecifiedNaN()));
-  MOZ_ASSERT(IsNaN(SpecificNaN(1, 17)));;
-  MOZ_ASSERT(IsNaN(SpecificNaN(0, 0xfffffffffff0fULL)));
-  MOZ_ASSERT(!IsNaN(0));
+  MOZ_ASSERT(IsNaN(UnspecifiedNaN<double>()));
+  MOZ_ASSERT(IsNaN(SpecificNaN<double>(1, 17)));;
+  MOZ_ASSERT(IsNaN(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+  MOZ_ASSERT(!IsNaN(0.0));
   MOZ_ASSERT(!IsNaN(-0.0));
   MOZ_ASSERT(!IsNaN(1.0));
-  MOZ_ASSERT(!IsNaN(PositiveInfinity()));
-  MOZ_ASSERT(!IsNaN(NegativeInfinity()));
+  MOZ_ASSERT(!IsNaN(PositiveInfinity<double>()));
+  MOZ_ASSERT(!IsNaN(NegativeInfinity<double>()));
 
-  MOZ_ASSERT(IsInfinite(PositiveInfinity()));
-  MOZ_ASSERT(IsInfinite(NegativeInfinity()));
-  MOZ_ASSERT(!IsInfinite(UnspecifiedNaN()));
-  MOZ_ASSERT(!IsInfinite(0));
+  MOZ_ASSERT(IsInfinite(PositiveInfinity<double>()));
+  MOZ_ASSERT(IsInfinite(NegativeInfinity<double>()));
+  MOZ_ASSERT(!IsInfinite(UnspecifiedNaN<double>()));
+  MOZ_ASSERT(!IsInfinite(0.0));
   MOZ_ASSERT(!IsInfinite(-0.0));
   MOZ_ASSERT(!IsInfinite(1.0));
 
-  MOZ_ASSERT(!IsFinite(PositiveInfinity()));
-  MOZ_ASSERT(!IsFinite(NegativeInfinity()));
-  MOZ_ASSERT(!IsFinite(UnspecifiedNaN()));
-  MOZ_ASSERT(IsFinite(0));
+  MOZ_ASSERT(!IsFinite(PositiveInfinity<double>()));
+  MOZ_ASSERT(!IsFinite(NegativeInfinity<double>()));
+  MOZ_ASSERT(!IsFinite(UnspecifiedNaN<double>()));
+  MOZ_ASSERT(IsFinite(0.0));
   MOZ_ASSERT(IsFinite(-0.0));
   MOZ_ASSERT(IsFinite(1.0));
 
-  MOZ_ASSERT(!IsNegative(PositiveInfinity()));
-  MOZ_ASSERT(IsNegative(NegativeInfinity()));
+  MOZ_ASSERT(!IsNegative(PositiveInfinity<double>()));
+  MOZ_ASSERT(IsNegative(NegativeInfinity<double>()));
   MOZ_ASSERT(IsNegative(-0.0));
   MOZ_ASSERT(!IsNegative(0.0));
   MOZ_ASSERT(IsNegative(-1.0));
   MOZ_ASSERT(!IsNegative(1.0));
 
-  MOZ_ASSERT(!IsNegativeZero(PositiveInfinity()));
-  MOZ_ASSERT(!IsNegativeZero(NegativeInfinity()));
-  MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 17)));;
-  MOZ_ASSERT(!IsNegativeZero(SpecificNaN(1, 0xfffffffffff0fULL)));
-  MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 17)));;
-  MOZ_ASSERT(!IsNegativeZero(SpecificNaN(0, 0xfffffffffff0fULL)));
-  MOZ_ASSERT(!IsNegativeZero(UnspecifiedNaN()));
+  MOZ_ASSERT(!IsNegativeZero(PositiveInfinity<double>()));
+  MOZ_ASSERT(!IsNegativeZero(NegativeInfinity<double>()));
+  MOZ_ASSERT(!IsNegativeZero(SpecificNaN<double>(1, 17)));;
+  MOZ_ASSERT(!IsNegativeZero(SpecificNaN<double>(1, 0xfffffffffff0fULL)));
+  MOZ_ASSERT(!IsNegativeZero(SpecificNaN<double>(0, 17)));;
+  MOZ_ASSERT(!IsNegativeZero(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+  MOZ_ASSERT(!IsNegativeZero(UnspecifiedNaN<double>()));
   MOZ_ASSERT(IsNegativeZero(-0.0));
   MOZ_ASSERT(!IsNegativeZero(0.0));
   MOZ_ASSERT(!IsNegativeZero(-1.0));
   MOZ_ASSERT(!IsNegativeZero(1.0));
 
   int32_t i;
-  MOZ_ASSERT(DoubleIsInt32(0.0, &i)); MOZ_ASSERT(i == 0);
-  MOZ_ASSERT(!DoubleIsInt32(-0.0, &i));
-  MOZ_ASSERT(DoubleEqualsInt32(0.0, &i)); MOZ_ASSERT(i == 0);
-  MOZ_ASSERT(DoubleEqualsInt32(-0.0, &i)); MOZ_ASSERT(i == 0);
-  MOZ_ASSERT(DoubleIsInt32(INT32_MIN, &i)); MOZ_ASSERT(i == INT32_MIN);
-  MOZ_ASSERT(DoubleIsInt32(INT32_MAX, &i)); MOZ_ASSERT(i == INT32_MAX);
-  MOZ_ASSERT(DoubleEqualsInt32(INT32_MIN, &i)); MOZ_ASSERT(i == INT32_MIN);
-  MOZ_ASSERT(DoubleEqualsInt32(INT32_MAX, &i)); MOZ_ASSERT(i == INT32_MAX);
-  MOZ_ASSERT(!DoubleIsInt32(0.5, &i));
-  MOZ_ASSERT(!DoubleIsInt32(double(INT32_MAX) + 0.1, &i));
-  MOZ_ASSERT(!DoubleIsInt32(double(INT32_MIN) - 0.1, &i));
-  MOZ_ASSERT(!DoubleIsInt32(NegativeInfinity(), &i));
-  MOZ_ASSERT(!DoubleIsInt32(PositiveInfinity(), &i));
-  MOZ_ASSERT(!DoubleIsInt32(UnspecifiedNaN(), &i));
-  MOZ_ASSERT(!DoubleEqualsInt32(0.5, &i));
-  MOZ_ASSERT(!DoubleEqualsInt32(double(INT32_MAX) + 0.1, &i));
-  MOZ_ASSERT(!DoubleEqualsInt32(double(INT32_MIN) - 0.1, &i));
-  MOZ_ASSERT(!DoubleEqualsInt32(NegativeInfinity(), &i));
-  MOZ_ASSERT(!DoubleEqualsInt32(PositiveInfinity(), &i));
-  MOZ_ASSERT(!DoubleEqualsInt32(UnspecifiedNaN(), &i));
+  MOZ_ASSERT(NumberIsInt32(0.0, &i)); MOZ_ASSERT(i == 0);
+  MOZ_ASSERT(!NumberIsInt32(-0.0, &i));
+  MOZ_ASSERT(NumberEqualsInt32(0.0, &i)); MOZ_ASSERT(i == 0);
+  MOZ_ASSERT(NumberEqualsInt32(-0.0, &i)); MOZ_ASSERT(i == 0);
+  MOZ_ASSERT(NumberIsInt32(double(INT32_MIN), &i)); MOZ_ASSERT(i == INT32_MIN);
+  MOZ_ASSERT(NumberIsInt32(double(INT32_MAX), &i)); MOZ_ASSERT(i == INT32_MAX);
+  MOZ_ASSERT(NumberEqualsInt32(double(INT32_MIN), &i)); MOZ_ASSERT(i == INT32_MIN);
+  MOZ_ASSERT(NumberEqualsInt32(double(INT32_MAX), &i)); MOZ_ASSERT(i == INT32_MAX);
+  MOZ_ASSERT(!NumberIsInt32(0.5, &i));
+  MOZ_ASSERT(!NumberIsInt32(double(INT32_MAX) + 0.1, &i));
+  MOZ_ASSERT(!NumberIsInt32(double(INT32_MIN) - 0.1, &i));
+  MOZ_ASSERT(!NumberIsInt32(NegativeInfinity<double>(), &i));
+  MOZ_ASSERT(!NumberIsInt32(PositiveInfinity<double>(), &i));
+  MOZ_ASSERT(!NumberIsInt32(UnspecifiedNaN<double>(), &i));
+  MOZ_ASSERT(!NumberEqualsInt32(0.5, &i));
+  MOZ_ASSERT(!NumberEqualsInt32(double(INT32_MAX) + 0.1, &i));
+  MOZ_ASSERT(!NumberEqualsInt32(double(INT32_MIN) - 0.1, &i));
+  MOZ_ASSERT(!NumberEqualsInt32(NegativeInfinity<double>(), &i));
+  MOZ_ASSERT(!NumberEqualsInt32(PositiveInfinity<double>(), &i));
+  MOZ_ASSERT(!NumberEqualsInt32(UnspecifiedNaN<double>(), &i));
 }
 
 static void
 TestFloatsAreApproximatelyEqual()
 {
   float epsilon = mozilla::detail::FuzzyEqualsEpsilon<float>::value();
   float lessThanEpsilon = epsilon / 2.0f;
   float moreThanEpsilon = epsilon * 2.0f;
@@ -251,20 +250,20 @@ TestFloatsAreApproximatelyEqual()
   MOZ_ASSERT(FuzzyEqualsMultiplicative(1.0f, 2.0f, 1.0f));
   MOZ_ASSERT(!FuzzyEqualsMultiplicative(1.0f, 2.0f, 0.1f));
 
   // "real world case"
   float oneThird = 10.0f / 3.0f;
   MOZ_ASSERT(FuzzyEqualsAdditive(10.0f, 3.0f * oneThird));
   MOZ_ASSERT(FuzzyEqualsMultiplicative(10.0f, 3.0f * oneThird));
   // NaN check
-  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificFloatNaN(1, 1), SpecificFloatNaN(1, 1)));
-  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificFloatNaN(1, 2), SpecificFloatNaN(0, 8)));
-  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificFloatNaN(1, 1), SpecificFloatNaN(1, 1)));
-  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificFloatNaN(1, 2), SpecificFloatNaN(0, 200)));
+  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 1), SpecificNaN<float>(1, 1)));
+  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 2), SpecificNaN<float>(0, 8)));
+  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 1), SpecificNaN<float>(1, 1)));
+  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 2), SpecificNaN<float>(0, 200)));
 }
 
 static void
 TestDoublesAreApproximatelyEqual()
 {
   double epsilon = mozilla::detail::FuzzyEqualsEpsilon<double>::value();
   double lessThanEpsilon = epsilon / 2.0;
   double moreThanEpsilon = epsilon * 2.0;
@@ -320,20 +319,20 @@ TestDoublesAreApproximatelyEqual()
   MOZ_ASSERT(FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 1.0));
   MOZ_ASSERT(!FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 0.1));
 
   // "real world case"
   double oneThird = 10.0 / 3.0;
   MOZ_ASSERT(FuzzyEqualsAdditive(10.0, 3.0 * oneThird));
   MOZ_ASSERT(FuzzyEqualsMultiplicative(10.0, 3.0 * oneThird));
   // NaN check
-  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificNaN(1, 1), SpecificNaN(1, 1)));
-  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificNaN(1, 2), SpecificNaN(0, 8)));
-  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificNaN(1, 1), SpecificNaN(1, 1)));
-  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificNaN(1, 2), SpecificNaN(0, 200)));
+  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 1), SpecificNaN<double>(1, 1)));
+  MOZ_ASSERT(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 2), SpecificNaN<double>(0, 8)));
+  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 1), SpecificNaN<double>(1, 1)));
+  MOZ_ASSERT(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 2), SpecificNaN<double>(0, 200)));
 }
 
 static void
 TestAreApproximatelyEqual()
 {
   TestFloatsAreApproximatelyEqual();
   TestDoublesAreApproximatelyEqual();
 }