Bug 1558538 - BigInt-to-Number conversion is rather borken. r=wingo, a=jcristau
authorJeff Walden <jwalden@mit.edu>
Wed, 19 Jun 2019 18:39:02 +0000
changeset 537051 42c717b471c5f9e02ab3f9cf07dcf8dd5bc20fee
parent 537050 6f87bc5460f76ce43c4db98ebf26744667d907c6
child 537052 17ac07e2e291bae9a37939f6b2c2b5f2ef6ab5e5
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswingo, jcristau
bugs1558538
milestone68.0
Bug 1558538 - BigInt-to-Number conversion is rather borken. r=wingo, a=jcristau Differential Revision: https://phabricator.services.mozilla.com/D34678
js/src/tests/non262/BigInt/Number-conversion-rounding.js
js/src/vm/BigIntType.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/BigInt/Number-conversion-rounding.js
@@ -0,0 +1,187 @@
+// |reftest| skip-if(!this.hasOwnProperty("BigInt"))
+// Any copyright is dedicated to the Public Domain.
+// https://creativecommons.org/licenses/publicdomain/
+
+/**
+ * Simple single-Digit on x64, double-Digit on x86 tests.
+ */
+
+assertEq(BigInt(Number(2n**53n - 2n)), 2n**53n - 2n);
+assertEq(BigInt(Number(2n**53n - 1n)), 2n**53n - 1n);
+assertEq(BigInt(Number(2n**53n)), 2n**53n);
+assertEq(BigInt(Number(2n**53n + 1n)), 2n**53n);
+assertEq(BigInt(Number(2n**53n + 2n)), 2n**53n + 2n);
+assertEq(BigInt(Number(2n**53n + 3n)), 2n**53n + 4n);
+assertEq(BigInt(Number(2n**53n + 4n)), 2n**53n + 4n);
+assertEq(BigInt(Number(2n**53n + 5n)), 2n**53n + 4n);
+assertEq(BigInt(Number(2n**53n + 6n)), 2n**53n + 6n);
+assertEq(BigInt(Number(2n**53n + 7n)), 2n**53n + 8n);
+assertEq(BigInt(Number(2n**53n + 8n)), 2n**53n + 8n);
+
+assertEq(BigInt(Number(2n**54n - 4n)), 2n**54n - 4n);
+assertEq(BigInt(Number(2n**54n - 3n)), 2n**54n - 4n);
+assertEq(BigInt(Number(2n**54n - 2n)), 2n**54n - 2n);
+assertEq(BigInt(Number(2n**54n - 1n)), 2n**54n);
+assertEq(BigInt(Number(2n**54n)), 2n**54n);
+assertEq(BigInt(Number(2n**54n + 1n)), 2n**54n);
+assertEq(BigInt(Number(2n**54n + 2n)), 2n**54n);
+assertEq(BigInt(Number(2n**54n + 3n)), 2n**54n + 4n);
+assertEq(BigInt(Number(2n**54n + 4n)), 2n**54n + 4n);
+assertEq(BigInt(Number(2n**54n + 5n)), 2n**54n + 4n);
+assertEq(BigInt(Number(2n**54n + 6n)), 2n**54n + 8n);
+assertEq(BigInt(Number(2n**54n + 7n)), 2n**54n + 8n);
+assertEq(BigInt(Number(2n**54n + 8n)), 2n**54n + 8n);
+
+assertEq(BigInt(Number(2n**55n - 8n)), 2n**55n - 8n);
+assertEq(BigInt(Number(2n**55n - 7n)), 2n**55n - 8n);
+assertEq(BigInt(Number(2n**55n - 6n)), 2n**55n - 8n);
+assertEq(BigInt(Number(2n**55n - 5n)), 2n**55n - 4n);
+assertEq(BigInt(Number(2n**55n - 4n)), 2n**55n - 4n);
+assertEq(BigInt(Number(2n**55n - 3n)), 2n**55n - 4n);
+assertEq(BigInt(Number(2n**55n - 2n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n - 1n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n + 1n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n + 2n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n + 3n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n + 4n)), 2n**55n);
+assertEq(BigInt(Number(2n**55n + 5n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 6n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 7n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 8n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 9n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 10n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 11n)), 2n**55n + 8n);
+assertEq(BigInt(Number(2n**55n + 12n)), 2n**55n + 16n);
+assertEq(BigInt(Number(2n**55n + 13n)), 2n**55n + 16n);
+assertEq(BigInt(Number(2n**55n + 14n)), 2n**55n + 16n);
+assertEq(BigInt(Number(2n**55n + 15n)), 2n**55n + 16n);
+assertEq(BigInt(Number(2n**55n + 16n)), 2n**55n + 16n);
+
+
+/**
+ * Simple double-Digit on x64, triple-Digit on x86 tests.
+ */
+
+// The tests below that aren't subtracting bits will have no bits in the
+// ultimate significand from the most-significant digit (because of the implicit
+// one being excluded).
+assertEq(BigInt(Number(2n**64n - 2n**11n)), 2n**64n - 2n**11n);
+assertEq(BigInt(Number(2n**64n - 2n**11n + 2n**10n - 1n)), 2n**64n - 2n**11n);
+assertEq(BigInt(Number(2n**64n - 2n**11n + 2n**10n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n - 2n**10n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n + 1n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n + 2n**5n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n + 2n**10n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n + 2n**11n)), 2n**64n);
+assertEq(BigInt(Number(2n**64n + 2n**11n + 1n)), 2n**64n + 2n**12n);
+assertEq(BigInt(Number(2n**64n + 2n**12n)), 2n**64n + 2n**12n);
+assertEq(BigInt(Number(2n**64n + 2n**12n + 1n)), 2n**64n + 2n**12n);
+assertEq(BigInt(Number(2n**64n + 2n**12n + 2n**5n)), 2n**64n + 2n**12n);
+assertEq(BigInt(Number(2n**64n + 2n**12n + 2n**10n)), 2n**64n + 2n**12n);
+assertEq(BigInt(Number(2n**64n + 2n**12n + 2n**11n - 1n)), 2n**64n + 2n**12n);
+assertEq(BigInt(Number(2n**64n + 2n**12n + 2n**11n)), 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**64n + 2n**12n + 2n**11n + 1n)), 2n**64n + 2n**13n);
+
+// These tests *will* have a bit from the most-significant digit in the ultimate
+// significand.
+assertEq(BigInt(Number(2n**65n - 2n**12n)), 2n**65n - 2n**12n);
+assertEq(BigInt(Number(2n**65n - 2n**12n + 2n**11n - 1n)), 2n**65n - 2n**12n);
+assertEq(BigInt(Number(2n**65n - 2n**12n + 2n**11n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n - 2n**11n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n + 1n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n + 2n**5n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n + 2n**11n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n + 2n**12n)), 2n**65n);
+assertEq(BigInt(Number(2n**65n + 2n**12n + 1n)), 2n**65n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**13n)), 2n**65n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**13n + 1n)), 2n**65n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**13n + 2n**5n)), 2n**65n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**13n + 2n**11n)), 2n**65n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**13n + 2n**12n - 1n)), 2n**65n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**13n + 2n**12n)), 2n**65n + 2n**14n);
+assertEq(BigInt(Number(2n**65n + 2n**13n + 2n**12n + 1n)), 2n**65n + 2n**14n);
+
+// ...and in these tests, the contributed bit from the most-significant digit
+// is additionally nonzero.
+assertEq(BigInt(Number(2n**65n + 2n**64n)), 2n**65n + 2n**64n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 1n)), 2n**65n + 2n**64n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**5n)), 2n**65n + 2n**64n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**11n)), 2n**65n + 2n**64n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**12n)), 2n**65n + 2n**64n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**12n + 1n)), 2n**65n + 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n)), 2n**65n + 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n + 1n)), 2n**65n + 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n + 2n**5n)), 2n**65n + 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n + 2n**11n)), 2n**65n + 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n + 2n**12n - 1n)), 2n**65n + 2n**64n + 2n**13n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n + 2n**12n)), 2n**65n + 2n**64n + 2n**14n);
+assertEq(BigInt(Number(2n**65n + 2n**64n + 2n**13n + 2n**12n + 1n)), 2n**65n + 2n**64n + 2n**14n);
+
+/**
+ * Versions of the testing above with all the high-order bits massively bumped
+ * upward to test that super-low bits, not just bits in high digits, are
+ * properly accounted for in rounding.
+ */
+
+// The tests below that aren't subtracting bits will have no bits in the
+// ultimate significand from the most-significant digit (because of the implicit
+// one being excluded).
+assertEq(BigInt(Number(2n**940n - 2n**887n + 1n)), 2n**940n - 2n**887n);
+assertEq(BigInt(Number(2n**940n - 2n**887n + 2n**886n - 1n)), 2n**940n - 2n**887n);
+assertEq(BigInt(Number(2n**940n - 2n**887n + 2n**886n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n - 2n**886n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n + 1n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n + 2n**880n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n + 2n**885n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n + 2n**887n)), 2n**940n);
+assertEq(BigInt(Number(2n**940n + 2n**887n + 1n)), 2n**940n + 2n**888n);
+assertEq(BigInt(Number(2n**940n + 2n**888n)), 2n**940n + 2n**888n);
+assertEq(BigInt(Number(2n**940n + 2n**888n + 1n)), 2n**940n + 2n**888n);
+assertEq(BigInt(Number(2n**940n + 2n**888n + 2n**5n)), 2n**940n + 2n**888n);
+assertEq(BigInt(Number(2n**940n + 2n**888n + 2n**12n)), 2n**940n + 2n**888n);
+assertEq(BigInt(Number(2n**940n + 2n**888n + 2n**887n - 1n)), 2n**940n + 2n**888n);
+assertEq(BigInt(Number(2n**940n + 2n**888n + 2n**887n)), 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**940n + 2n**888n + 2n**887n + 1n)), 2n**940n + 2n**889n);
+
+// These tests *will* have a bit from the most-significant digit in the ultimate
+// significand.
+assertEq(BigInt(Number(2n**941n - 2n**888n)), 2n**941n - 2n**888n);
+assertEq(BigInt(Number(2n**941n - 2n**888n + 2n**887n - 1n)), 2n**941n - 2n**888n);
+assertEq(BigInt(Number(2n**941n - 2n**888n + 2n**887n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n - 2n**887n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n + 1n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n + 2n**881n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n + 2n**886n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n + 2n**888n)), 2n**941n);
+assertEq(BigInt(Number(2n**941n + 2n**888n + 1n)), 2n**941n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**889n)), 2n**941n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**889n + 1n)), 2n**941n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**889n + 2n**5n)), 2n**941n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**889n + 2n**12n)), 2n**941n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**889n + 2n**888n - 1n)), 2n**941n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**889n + 2n**888n)), 2n**941n + 2n**890n);
+assertEq(BigInt(Number(2n**941n + 2n**889n + 2n**888n + 1n)), 2n**941n + 2n**890n);
+
+// ...and in these tests, the contributed bit from the most-significant digit
+// is additionally nonzero.
+assertEq(BigInt(Number(2n**941n + 2n**940n)), 2n**941n + 2n**940n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 1n)), 2n**941n + 2n**940n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**881n)), 2n**941n + 2n**940n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**886n)), 2n**941n + 2n**940n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**888n)), 2n**941n + 2n**940n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**888n + 1n)), 2n**941n + 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n)), 2n**941n + 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n + 1n)), 2n**941n + 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n + 2n**5n)), 2n**941n + 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n + 2n**12n)), 2n**941n + 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n + 2n**888n - 1n)), 2n**941n + 2n**940n + 2n**889n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n + 2n**888n)), 2n**941n + 2n**940n + 2n**890n);
+assertEq(BigInt(Number(2n**941n + 2n**940n + 2n**889n + 2n**888n + 1n)), 2n**941n + 2n**940n + 2n**890n);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/src/vm/BigIntType.cpp
+++ b/js/src/vm/BigIntType.cpp
@@ -2719,18 +2719,19 @@ double BigInt::numberValue(BigInt* x) {
   constexpr uint8_t ExponentShift = Double::kExponentShift;
   constexpr uint8_t SignificandWidth = Double::kSignificandWidth;
   constexpr unsigned ExponentBias = Double::kExponentBias;
   constexpr uint8_t SignShift = Double::kExponentWidth + SignificandWidth;
 
   size_t length = x->digitLength();
   MOZ_ASSERT(length != 0);
 
-  // Fast path for the likely-common case of up to a uint64_t of magnitude
-  // that doesn't exceed integral precision in IEEE-754.
+  // Fast path for the likely-common case of up to a uint64_t of magnitude not
+  // exceeding integral precision in IEEE-754.  (Note that we *depend* on this
+  // optimization being performed further down.)
   if (length <= 64 / DigitBits) {
     uint64_t magnitude = x->digit(0);
     if (DigitBits == 32 && length > 1) {
       magnitude |= uint64_t(x->digit(1)) << 32;
     }
     const uint64_t MaxIntegralPrecisionDouble = uint64_t(1)
                                                 << (SignificandWidth + 1);
     if (magnitude <= MaxIntegralPrecisionDouble) {
@@ -2753,72 +2754,175 @@ double BigInt::numberValue(BigInt* x) {
   // Otherwise munge the most significant bits of the number into proper
   // position in an IEEE-754 double and go to town.
 
   // Omit the most significant bit: the IEEE-754 format includes this bit
   // implicitly for all double-precision integers.
   const uint8_t msdIgnoredBits = msdLeadingZeroes + 1;
   const uint8_t msdIncludedBits = DigitBits - msdIgnoredBits;
 
-  uint8_t bitsFilled = msdIncludedBits;
-
-  // Shift `msd`'s contributed bits upward to remove high-order zeroes and
-  // the highest set bit (which is implicit in IEEE-754 integral values so
-  // must be removed) and to add low-order zeroes.
+  // We compute the final mantissa of the result, shifted upward to the top of
+  // the `uint64_t` space -- plus an extra bit to detect potential rounding.
+  constexpr uint8_t BitsNeededForShiftedMantissa = SignificandWidth + 1;
+
+  // Shift `msd`'s contributed bits upward to remove high-order zeroes and the
+  // highest set bit (which is implicit in IEEE-754 integral values so must be
+  // removed) and to add low-order zeroes.  (Lower-order garbage bits are
+  // discarded when `shiftedMantissa` is converted to a real mantissa.)
   uint64_t shiftedMantissa =
       msdIncludedBits == 0 ? 0 : uint64_t(msd) << (64 - msdIncludedBits);
 
-  // Add in bits from the next one or two digits if `msd` didn't contain all
-  // bits necessary to define the result.  (The extra bit allows us to
-  // properly round an inexact overall result.)  Any lower bits that are
-  // uselessly set will be shifted away when `shiftedMantissa` is converted to
-  // a real mantissa.
-  if (bitsFilled < SignificandWidth + 1) {
+  // If the extra bit is set, correctly rounding the result may require
+  // examining all lower-order bits.  Also compute 1) the index of the Digit
+  // storing the extra bit, and 2) whether bits beneath the extra bit in that
+  // Digit are nonzero so we can round if needed.
+  size_t digitContainingExtraBit;
+  Digit bitsBeneathExtraBitInDigitContainingExtraBit;
+
+  // Add shifted bits to `shiftedMantissa` until we have a complete mantissa and
+  // an extra bit.
+  if (msdIncludedBits >= BitsNeededForShiftedMantissa) {
+    //       DigitBits=64 (necessarily for msdIncludedBits ≥ SignificandWidth+1;
+    //            |        C++ compiler range analysis ought eliminate this
+    //            |        check on 32-bit)
+    //   _________|__________
+    //  /                    |
+    //        msdIncludedBits
+    //      ________|________
+    //     /                 |
+    // [001···················|
+    //  \_/\_____________/\__|
+    //   |            |    |
+    // msdIgnoredBits |   bits below the extra bit (may be no bits)
+    //      BitsNeededForShiftedMantissa=SignificandWidth+1
+    digitContainingExtraBit = length - 1;
+
+    const uint8_t countOfBitsInDigitBelowExtraBit =
+        DigitBits - BitsNeededForShiftedMantissa - msdIgnoredBits;
+    bitsBeneathExtraBitInDigitContainingExtraBit =
+        msd & ((Digit(1) << countOfBitsInDigitBelowExtraBit) - 1);
+  } else {
     MOZ_ASSERT(length >= 2,
                "single-Digit numbers with this few bits should have been "
                "handled by the fast-path above");
 
     Digit second = x->digit(length - 2);
-    if (DigitBits == 32) {
+    if (DigitBits == 64) {
+      shiftedMantissa |= second >> msdIncludedBits;
+
+      digitContainingExtraBit = length - 2;
+
+      //  msdIncludedBits + DigitBits
+      //      ________|_________
+      //     /                  |
+      //             DigitBits=64
+      // msdIncludedBits    |
+      //      __|___   _____|___
+      //     /      \ /         |
+      // [001········|···········|
+      //  \_/\_____________/\___|
+      //   |            |     |
+      // msdIgnoredBits | bits below the extra bit (always more than one)
+      //                |
+      //      BitsNeededForShiftedMantissa=SignificandWidth+1
+      const uint8_t countOfBitsInSecondDigitBelowExtraBit =
+          (msdIncludedBits + DigitBits) - BitsNeededForShiftedMantissa;
+
+      bitsBeneathExtraBitInDigitContainingExtraBit =
+          second << (DigitBits - countOfBitsInSecondDigitBelowExtraBit);
+    } else {
       shiftedMantissa |= uint64_t(second) << msdIgnoredBits;
-      bitsFilled += DigitBits;
-
-      // Add in bits from another digit, if any, if we still have unfilled
-      // significand bits.
-      if (bitsFilled < SignificandWidth + 1 && length >= 3) {
+
+      if (msdIncludedBits + DigitBits >= BitsNeededForShiftedMantissa) {
+        digitContainingExtraBit = length - 2;
+
+        //  msdIncludedBits + DigitBits
+        //      ______|________
+        //     /               |
+        //             DigitBits=32
+        // msdIncludedBits |
+        //      _|_   _____|___
+        //     /   \ /         |
+        // [001·····|···········|
+        //     \___________/\__|
+        //          |        |
+        //          |      bits below the extra bit (may be no bits)
+        //      BitsNeededForShiftedMantissa=SignificandWidth+1
+        const uint8_t countOfBitsInSecondDigitBelowExtraBit =
+            (msdIncludedBits + DigitBits) - BitsNeededForShiftedMantissa;
+
+        bitsBeneathExtraBitInDigitContainingExtraBit =
+            second & ((Digit(1) << countOfBitsInSecondDigitBelowExtraBit) - 1);
+      } else {
+        MOZ_ASSERT(length >= 3,
+                   "we must have at least three digits here, because "
+                   "`msdIncludedBits + 32 < BitsNeededForShiftedMantissa` "
+                   "guarantees `x < 2**53` -- and therefore the "
+                   "MaxIntegralPrecisionDouble optimization above will have "
+                   "handled two-digit cases");
+
         Digit third = x->digit(length - 3);
         shiftedMantissa |= uint64_t(third) >> msdIncludedBits;
-        // The second and third 32-bit digits contributed 64 bits total, filling
-        // well beyond the mantissa.
-        bitsFilled = 64;
+
+        digitContainingExtraBit = length - 3;
+
+        //    msdIncludedBits + DigitBits + DigitBits
+        //      ____________|______________
+        //     /                           |
+        //             DigitBits=32
+        // msdIncludedBits |     DigitBits=32
+        //      _|_   _____|___   ____|____
+        //     /   \ /         \ /         |
+        // [001·····|···········|···········|
+        //     \____________________/\_____|
+        //               |               |
+        //               |      bits below the extra bit
+        //      BitsNeededForShiftedMantissa=SignificandWidth+1
+        static_assert(2 * DigitBits > BitsNeededForShiftedMantissa,
+                      "two 32-bit digits should more than fill a mantissa");
+        const uint8_t countOfBitsInThirdDigitBelowExtraBit =
+            msdIncludedBits + 2 * DigitBits - BitsNeededForShiftedMantissa;
+
+        // Shift out the mantissa bits and the extra bit.
+        bitsBeneathExtraBitInDigitContainingExtraBit =
+            third << (DigitBits - countOfBitsInThirdDigitBelowExtraBit);
       }
-    } else {
-      shiftedMantissa |= second >> msdIncludedBits;
-      // A full 64-bit digit's worth of bits (some from the most significant
-      // digit, the rest from the next) fills well beyond the mantissa.
-      bitsFilled = 64;
     }
   }
 
-  // Round the overall result, if necessary.  (It's possible we don't need to
-  // round -- the number might not have enough bits to round.)
-  if (bitsFilled >= SignificandWidth + 1) {
-    constexpr uint64_t LeastSignificantBit = uint64_t(1)
-                                             << (64 - SignificandWidth);
-    constexpr uint64_t ExtraBit = LeastSignificantBit >> 1;
-
-    // When the first bit outside the significand is set, the overall value
-    // is rounded: downward (i.e. no change to the bits) if the least
-    // significant bit in the significand is zero, upward if it instead is
-    // one.
-    if ((shiftedMantissa & ExtraBit) &&
-        (shiftedMantissa & LeastSignificantBit)) {
-      // We're rounding upward: add to the significand bits.  If they
-      // overflow, the exponent must also be increased.  If *that*
-      // overflows, return the appropriate infinity.
+  constexpr uint64_t LeastSignificantBit = uint64_t(1)
+                                           << (64 - SignificandWidth);
+  constexpr uint64_t ExtraBit = LeastSignificantBit >> 1;
+
+  // The extra bit must be set for rounding to change the mantissa.
+  if ((shiftedMantissa & ExtraBit) != 0) {
+    bool shouldRoundUp;
+    if (shiftedMantissa & LeastSignificantBit) {
+      // If the lowest mantissa bit is set, it doesn't matter what lower bits
+      // are: nearest-even rounds up regardless.
+      shouldRoundUp = true;
+    } else {
+      // If the lowest mantissa bit is unset, *all* lower bits are relevant.
+      // All-zero bits below the extra bit situates `x` halfway between two
+      // values, and the nearest *even* value lies downward.  But if any bit
+      // below the extra bit is set, `x` is closer to the rounded-up value.
+      shouldRoundUp = bitsBeneathExtraBitInDigitContainingExtraBit != 0;
+      if (!shouldRoundUp) {
+        while (digitContainingExtraBit-- > 0) {
+          if (x->digit(digitContainingExtraBit) != 0) {
+            shouldRoundUp = true;
+            break;
+          }
+        }
+      }
+    }
+
+    if (shouldRoundUp) {
+      // Add one to the significand bits.  If they overflow, the exponent must
+      // also be increased.  If *that* overflows, return the correct infinity.
       uint64_t before = shiftedMantissa;
       shiftedMantissa += ExtraBit;
       if (shiftedMantissa < before) {
         exponent++;
         if (exponent > ExponentBias) {
           return x->isNegative() ? NegativeInfinity<double>()
                                  : PositiveInfinity<double>();
         }