Bug 1447668. r=jorendorff
☠☠ backed out by e233cb06379d ☠ ☠
authorJeff Walden <jwalden@mit.edu>
Tue, 27 Mar 2018 14:01:43 -0700
changeset 466853 a30cf37ddcabae12cac3ff12179f568ad3084d11
parent 466852 dceb884bab4dd0486d73c890cfcaf70b7e83ab94
child 466854 fde6a66eb3170c0211ba97dcb3b43b4de45e23d8
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1447668
milestone61.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 1447668. r=jorendorff
js/public/Conversions.h
js/src/ctypes/CTypes.cpp
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testToIntWidth.cpp
js/src/jsapi-tests/testToSignedOrUnsignedInteger.cpp
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -281,37 +281,35 @@ ToObject(JSContext* cx, HandleValue v)
 {
     detail::AssertArgumentsAreSane(cx, v);
 
     if (v.isObject())
         return &v.toObject();
     return js::ToObjectSlow(cx, v, false);
 }
 
-namespace detail {
-
-/*
- * Convert a double value to ResultType (an unsigned integral type) using
+/**
+ * Convert a double value to UnsignedInteger (an unsigned integral type) using
  * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
  * ToInt32 converts to int32_t).
  *
  *   If d is infinite or NaN, return 0.
- *   Otherwise compute d2 = sign(d) * floor(abs(d)), and return the ResultType
- *   value congruent to d2 mod 2**(bit width of ResultType).
+ *   Otherwise compute d2 = sign(d) * floor(abs(d)), and return the
+ *   UnsignedInteger value congruent to d2 % 2**(bit width of UnsignedInteger).
  *
  * The algorithm below is inspired by that found in
- * <http://trac.webkit.org/changeset/67825/trunk/JavaScriptCore/runtime/JSValue.cpp>
+ * <https://trac.webkit.org/changeset/67825/webkit/trunk/JavaScriptCore/runtime/JSValue.cpp>
  * but has been generalized to all integer widths.
  */
-template<typename ResultType>
-inline ResultType
-ToUintWidth(double d)
+template<typename UnsignedInteger>
+inline UnsignedInteger
+ToUnsignedInteger(double d)
 {
-    static_assert(mozilla::IsUnsigned<ResultType>::value,
-                  "ResultType must be an unsigned type");
+    static_assert(mozilla::IsUnsigned<UnsignedInteger>::value,
+                  "UnsignedInteger must be an unsigned type");
 
     uint64_t bits = mozilla::BitwiseCast<uint64_t>(d);
     unsigned DoubleExponentShift = mozilla::FloatingPoint<double>::kExponentShift;
 
     // Extract the exponent component.  (Be careful here!  It's not technically
     // the exponent in NaN, infinities, and subnormals.)
     int_fast16_t exp =
         int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >> DoubleExponentShift) -
@@ -320,33 +318,33 @@ ToUintWidth(double d)
     // If the exponent's less than zero, abs(d) < 1, so the result is 0.  (This
     // also handles subnormals.)
     if (exp < 0)
         return 0;
 
     uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp);
 
     // If the exponent is greater than or equal to the bits of precision of a
-    // double plus ResultType's width, the number is either infinite, NaN, or
-    // too large to have lower-order bits in the congruent value.  (Example:
+    // double plus UnsignedInteger's width, the number is either infinite, NaN,
+    // or too large to have lower-order bits in the congruent value.  (Example:
     // 2**84 is exactly representable as a double.  The next exact double is
-    // 2**84 + 2**32.  Thus if ResultType is int32_t, an exponent >= 84 implies
-    // floor(abs(d)) == 0 mod 2**32.)  Return 0 in all these cases.
-    const size_t ResultWidth = CHAR_BIT * sizeof(ResultType);
+    // 2**84 + 2**32.  Thus if UnsignedInteger is uint32_t, an exponent >= 84
+    // implies floor(abs(d)) == 0 mod 2**32.)  Return 0 in all these cases.
+    constexpr size_t ResultWidth = CHAR_BIT * sizeof(UnsignedInteger);
     if (exponent >= DoubleExponentShift + ResultWidth)
         return 0;
 
     // The significand contains the bits that will determine the final result.
     // Shift those bits left or right, according to the exponent, to their
     // locations in the unsigned binary representation of floor(abs(d)).
-    static_assert(sizeof(ResultType) <= sizeof(uint64_t),
-                  "Left-shifting below would lose upper bits");
-    ResultType result = (exponent > DoubleExponentShift)
-                        ? ResultType(bits << (exponent - DoubleExponentShift))
-                        : ResultType(bits >> (DoubleExponentShift - exponent));
+    static_assert(sizeof(UnsignedInteger) <= sizeof(uint64_t),
+                  "left-shifting below would lose upper bits");
+    UnsignedInteger result = (exponent > DoubleExponentShift)
+                             ? UnsignedInteger(bits << (exponent - DoubleExponentShift))
+                             : UnsignedInteger(bits >> (DoubleExponentShift - exponent));
 
     // Two further complications remain.  First, |result| may contain bogus
     // sign/exponent bits.  Second, IEEE-754 numbers' significands (excluding
     // subnormals, but we already handled those) have an implicit leading 1
     // which may affect the final result.
     //
     // It may appear that there's complexity here depending on how ResultWidth
     // and DoubleExponentShift relate, but it turns out there's not.
@@ -363,47 +361,46 @@ ToUintWidth(double d)
     //   bogus bits in |result|.  This implies |exponent < ResultWidth|.  Any
     //   right-shift less than |ResultWidth| does too, which implies
     //   |DoubleExponentShift - ResultWidth < exponent|.  By assumption, then,
     //   |exponent| is negative, but we excluded that above.  So bogus bits
     //   need only |exponent < ResultWidth|.
     //   The implicit leading bit matters identically to the other case, so
     //   again, |exponent < ResultWidth|.
     if (exponent < ResultWidth) {
-        ResultType implicitOne = ResultType(1) << exponent;
+        UnsignedInteger implicitOne = UnsignedInteger(1) << exponent;
         result &= implicitOne - 1; // remove bogus bits
         result += implicitOne; // add the implicit bit
     }
 
     // Compute the congruent value in the signed range.
     return (bits & mozilla::FloatingPoint<double>::kSignBit) ? ~result + 1 : result;
 }
 
-template<typename ResultType>
-inline ResultType
-ToIntWidth(double d)
+template<typename SignedInteger>
+inline SignedInteger
+ToSignedInteger(double d)
 {
-    static_assert(mozilla::IsSigned<ResultType>::value,
-                  "ResultType must be a signed type");
+    static_assert(mozilla::IsSigned<SignedInteger>::value,
+                  "SignedInteger must be a signed type");
 
-    using UnsignedResult = typename mozilla::MakeUnsigned<ResultType>::Type;
-    UnsignedResult u = ToUintWidth<UnsignedResult>(d);
+    using UnsignedInteger = typename mozilla::MakeUnsigned<SignedInteger>::Type;
+    UnsignedInteger u = ToUnsignedInteger<UnsignedInteger>(d);
 
     return mozilla::WrapToSigned(u);
 }
 
-} // namespace detail
+// clang crashes compiling this when targeting arm:
+// https://llvm.org/bugs/show_bug.cgi?id=22974
+#if defined (__arm__) && defined (__GNUC__) && !defined(__clang__)
 
-/* ES5 9.5 ToInt32 (specialized for doubles). */
+template<>
 inline int32_t
-ToInt32(double d)
+ToSignedInteger<int32_t>(double d)
 {
-    // clang crashes compiling this when targeting arm:
-    // https://llvm.org/bugs/show_bug.cgi?id=22974
-#if defined (__arm__) && defined (__GNUC__) && !defined(__clang__)
     int32_t i;
     uint32_t    tmp0;
     uint32_t    tmp1;
     uint32_t    tmp2;
     asm (
     // We use a pure integer solution here. In the 'softfp' ABI, the argument
     // will start in r0 and r1, and VFP can't do all of the necessary ECMA
     // conversions by itself so some integer code will be required anyway. A
@@ -514,64 +511,101 @@ ToInt32(double d)
     // will result in a conversion of '0'.
 "   mov     %0, #0\n"
 "9:\n"
     : "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2), "=&r" (d)
     : "4" (d)
     : "cc"
         );
     return i;
-#else
-    return detail::ToIntWidth<int32_t>(d);
-#endif
 }
 
-/* ES5 9.6 (specialized for doubles). */
-inline uint32_t
-ToUint32(double d)
+#endif // defined (__arm__) && defined (__GNUC__) && !defined(__clang__)
+
+namespace detail {
+
+template<typename IntegerType,
+         bool IsUnsigned = mozilla::IsUnsigned<IntegerType>::value>
+struct ToSignedOrUnsignedInteger;
+
+template<typename IntegerType>
+struct ToSignedOrUnsignedInteger<IntegerType, true>
 {
-    return detail::ToUintWidth<uint32_t>(d);
+    static IntegerType compute(double d) {
+        return ToUnsignedInteger<IntegerType>(d);
+    }
+};
+
+template<typename IntegerType>
+struct ToSignedOrUnsignedInteger<IntegerType, false>
+{
+    static IntegerType compute(double d) {
+        return ToSignedInteger<IntegerType>(d);
+    }
+};
+
+} // namespace detail
+
+template<typename IntegerType>
+inline IntegerType
+ToSignedOrUnsignedInteger(double d)
+{
+    return detail::ToSignedOrUnsignedInteger<IntegerType>::compute(d);
 }
 
 /* WEBIDL 4.2.4 */
 inline int8_t
 ToInt8(double d)
 {
-    return detail::ToIntWidth<int8_t>(d);
+    return ToSignedInteger<int8_t>(d);
 }
 
 /* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */
 inline int8_t
 ToUint8(double d)
 {
-    return detail::ToUintWidth<uint8_t>(d);
+    return ToUnsignedInteger<uint8_t>(d);
 }
 
 /* WEBIDL 4.2.6 */
 inline int16_t
 ToInt16(double d)
 {
-    return detail::ToIntWidth<int16_t>(d);
+    return ToSignedInteger<int16_t>(d);
 }
 
 inline uint16_t
 ToUint16(double d)
 {
-    return detail::ToUintWidth<uint16_t>(d);
+    return ToUnsignedInteger<uint16_t>(d);
+}
+
+/* ES5 9.5 ToInt32 (specialized for doubles). */
+inline int32_t
+ToInt32(double d)
+{
+    return ToSignedInteger<int32_t>(d);
+}
+
+/* ES5 9.6 (specialized for doubles). */
+inline uint32_t
+ToUint32(double d)
+{
+    return ToUnsignedInteger<uint32_t>(d);
 }
 
 /* WEBIDL 4.2.10 */
 inline int64_t
 ToInt64(double d)
 {
-    return detail::ToIntWidth<int64_t>(d);
+    return ToSignedInteger<int64_t>(d);
 }
 
 /* WEBIDL 4.2.11 */
 inline uint64_t
 ToUint64(double d)
 {
-    return detail::ToUintWidth<uint64_t>(d);
+    return ToUnsignedInteger<uint64_t>(d);
 }
 
 } // namespace JS
 
 #endif /* js_Conversions_h */
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -5,32 +5,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ctypes/CTypes.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Vector.h"
-
-#include <limits>
-#include <math.h>
-#include <stdint.h>
+#include "mozilla/WrappingOperations.h"
+
+#if defined(XP_UNIX)
+# include <errno.h>
+#endif
 #if defined(XP_WIN)
 # include <float.h>
 #endif
 #if defined(SOLARIS)
 # include <ieeefp.h>
 #endif
+#include <limits>
+#include <math.h>
+#include <stdint.h>
 #ifdef HAVE_SSIZE_T
 # include <sys/types.h>
 #endif
-#if defined(XP_UNIX)
-# include <errno.h>
-#endif
+#include <type_traits>
 
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
@@ -2570,64 +2572,62 @@ JS_STATIC_ASSERT(sizeof(int) == 4);
 JS_STATIC_ASSERT(sizeof(unsigned) == 4);
 JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
 JS_STATIC_ASSERT(sizeof(long long) == 8);
 JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
 JS_STATIC_ASSERT(sizeof(float) == 4);
 JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
 JS_STATIC_ASSERT(numeric_limits<double>::is_signed);
 
-// Templated helper to convert FromType to TargetType, for the default case
-// where the trivial POD constructor will do.
-template<class TargetType, class FromType>
-struct ConvertImpl {
-  static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
-    return TargetType(d);
-  }
-};
-
-#ifdef _MSC_VER
-// MSVC can't perform double to unsigned __int64 conversion when the
-// double is greater than 2^63 - 1. Help it along a little.
-template<>
-struct ConvertImpl<uint64_t, double> {
-  static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
-    return d > 0x7fffffffffffffffui64 ?
-           uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
-           uint64_t(d);
+template<typename TargetType,
+         typename FromType,
+         bool FromIsIntegral = std::is_integral<FromType>::value>
+struct ConvertImpl;
+
+template<typename TargetType, typename FromType>
+struct ConvertImpl<TargetType, FromType, false>
+{
+  static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
+    return JS::ToSignedOrUnsignedInteger<TargetType>(input);
   }
 };
-#endif
-
-// C++ doesn't guarantee that exact values are the only ones that will
-// round-trip. In fact, on some platforms, including SPARC, there are pairs of
-// values, a uint64_t and a double, such that neither value is exactly
-// representable in the other type, but they cast to each other.
-#if defined(SPARC) || defined(__powerpc__)
-// Simulate x86 overflow behavior
+
+template<typename TargetType>
+inline TargetType
+ConvertUnsignedTargetTo(typename std::make_unsigned<TargetType>::type input)
+{
+  return std::is_signed<TargetType>::value ? mozilla::WrapToSigned(input) : input;
+}
+
 template<>
-struct ConvertImpl<uint64_t, double> {
-  static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
-    return d >= 0xffffffffffffffff ?
-           0x8000000000000000 : uint64_t(d);
+inline char16_t
+ConvertUnsignedTargetTo<char16_t>(char16_t input)
+{
+  // mozilla::WrapToSigned can't be used on char16_t.
+  return input;
+}
+
+template<typename TargetType, typename FromType>
+struct ConvertImpl<TargetType, FromType, true>
+{
+  static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
+    using UnsignedTargetType = typename std::make_unsigned<TargetType>::type;
+    auto resultUnsigned = static_cast<UnsignedTargetType>(input);
+
+    return ConvertUnsignedTargetTo<TargetType>(resultUnsigned);
   }
 };
 
-template<>
-struct ConvertImpl<int64_t, double> {
-  static MOZ_ALWAYS_INLINE int64_t Convert(double d) {
-    return d >= 0x7fffffffffffffff ?
-           0x8000000000000000 : int64_t(d);
-  }
-};
-#endif
-
 template<class TargetType, class FromType>
 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d)
 {
+  static_assert(std::is_integral<FromType>::value !=
+                std::is_floating_point<FromType>::value,
+                "should only be converting from floating/integral type");
+
   return ConvertImpl<TargetType, FromType>::Convert(d);
 }
 
 template<class TargetType, class FromType>
 static MOZ_ALWAYS_INLINE bool IsAlwaysExact()
 {
   // Return 'true' if TargetType can always exactly represent FromType.
   // This means that:
@@ -2680,18 +2680,18 @@ struct IsExactImpl<TargetType, FromType,
   }
 };
 
 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
 // is an exact representation of 'i'.
 template<class TargetType, class FromType>
 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
 {
-  // Require that TargetType is integral, to simplify conversion.
-  JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
+  static_assert(std::numeric_limits<TargetType>::is_exact,
+                "TargetType must be exact to simplify conversion");
 
   *result = Convert<TargetType>(i);
 
   // See if we can avoid a dynamic check.
   if (IsAlwaysExact<TargetType, FromType>())
     return true;
 
   // Return 'true' if 'i' is exactly representable in 'TargetType'.
@@ -3100,19 +3100,21 @@ SizeTojsval(JSContext* cx, size_t size, 
 // Forcefully convert val to IntegerType when explicitly requested.
 template<class IntegerType>
 static bool
 jsvalToIntegerExplicit(HandleValue val, IntegerType* result)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
   if (val.isDouble()) {
-    // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
+    // Convert using ToInt32-style semantics: non-finite numbers become 0, and
+    // everything else rounds toward zero then maps into |IntegerType| with
+    // wraparound semantics.
     double d = val.toDouble();
-    *result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
+    *result = JS::ToSignedOrUnsignedInteger<IntegerType>(d);
     return true;
   }
   if (val.isObject()) {
     // Convert Int64 and UInt64 values by C-style cast.
     JSObject* obj = &val.toObject();
     if (Int64::IsInt64(obj)) {
       int64_t i = Int64Base::GetInt(obj);
       *result = IntegerType(i);
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -94,17 +94,17 @@ UNIFIED_SOURCES += [
     'testSourcePolicy.cpp',
     'testStringBuffer.cpp',
     'testStructuredClone.cpp',
     'testSymbol.cpp',
     'testThreadingConditionVariable.cpp',
     'testThreadingExclusiveData.cpp',
     'testThreadingMutex.cpp',
     'testThreadingThread.cpp',
-    'testToIntWidth.cpp',
+    'testToSignedOrUnsignedInteger.cpp',
     'testTypedArrays.cpp',
     'testUbiNode.cpp',
     'testUncaughtSymbol.cpp',
     'testUTF8.cpp',
     'testWasmLEB128.cpp',
     'testWeakMap.cpp',
     'testXDR.cpp',
 ]
rename from js/src/jsapi-tests/testToIntWidth.cpp
rename to js/src/jsapi-tests/testToSignedOrUnsignedInteger.cpp
--- a/js/src/jsapi-tests/testToIntWidth.cpp
+++ b/js/src/jsapi-tests/testToSignedOrUnsignedInteger.cpp
@@ -6,64 +6,64 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <math.h>
 
 #include "js/Conversions.h"
 
 #include "jsapi-tests/tests.h"
 
-using JS::detail::ToIntWidth;
-using JS::detail::ToUintWidth;
+using JS::ToSignedInteger;
+using JS::ToUnsignedInteger;
 
 BEGIN_TEST(testToUint8TwiceUint8Range)
 {
     double d = -256;
     uint8_t expected = 0;
     do {
-        CHECK(ToUintWidth<uint8_t>(d) == expected);
+        CHECK(ToUnsignedInteger<uint8_t>(d) == expected);
 
         d++;
         expected++;
     } while (d <= 256);
     return true;
 }
 END_TEST(testToUint8TwiceUint8Range)
 
 BEGIN_TEST(testToInt8)
 {
     double d = -128;
     int8_t expected = -128;
     do {
-        CHECK(ToIntWidth<int8_t>(d) == expected);
+        CHECK(ToSignedInteger<int8_t>(d) == expected);
 
         d++;
         expected++;
     } while (expected < 127);
     return true;
 }
 END_TEST(testToInt8)
 
 BEGIN_TEST(testToUint32Large)
 {
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83) + pow(2.0, 31)) == (1U << 31));
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83) + 2 * pow(2.0, 31)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83) + 3 * pow(2.0, 31)) == (1U << 31));
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 84)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 84) + pow(2.0, 31)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 84) + pow(2.0, 32)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83) + pow(2.0, 31)) == (1U << 31));
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83) + 2 * pow(2.0, 31)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83) + 3 * pow(2.0, 31)) == (1U << 31));
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 84)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 84) + pow(2.0, 31)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 84) + pow(2.0, 32)) == 0);
     return true;
 }
 END_TEST(testToUint32Large)
 
 BEGIN_TEST(testToUint64Large)
 {
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115) + pow(2.0, 63)) == (1ULL << 63));
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115) + 2 * pow(2.0, 63)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115) + 3 * pow(2.0, 63)) == (1ULL << 63));
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 116)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 116) + pow(2.0, 63)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 116) + pow(2.0, 64)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115) + pow(2.0, 63)) == (1ULL << 63));
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115) + 2 * pow(2.0, 63)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115) + 3 * pow(2.0, 63)) == (1ULL << 63));
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 116)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 116) + pow(2.0, 63)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 116) + pow(2.0, 64)) == 0);
     return true;
 }
 END_TEST(testToUint64Large)