tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Int128.cpp (6923B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "vm/Int128.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/Casting.h"
     11 #include "mozilla/FloatingPoint.h"
     12 #include "mozilla/MathAlgorithms.h"
     13 
     14 #include <stdint.h>
     15 
     16 using namespace js;
     17 
     18 double Uint128::toDouble(const Uint128& x, bool negative) {
     19  // Simplified version of |BigInt::numberValue()| for DigitBits=64. See the
     20  // comments in |BigInt::numberValue()| for how this code works.
     21 
     22  using Double = mozilla::FloatingPoint<double>;
     23  constexpr uint8_t ExponentShift = Double::kExponentShift;
     24  constexpr uint8_t SignificandWidth = Double::kSignificandWidth;
     25  constexpr unsigned ExponentBias = Double::kExponentBias;
     26  constexpr uint8_t SignShift = Double::kExponentWidth + SignificandWidth;
     27 
     28  constexpr uint64_t MaxIntegralPrecisionDouble = uint64_t(1)
     29                                                  << (SignificandWidth + 1);
     30 
     31  // We compute the final mantissa of the result, shifted upward to the top of
     32  // the `uint64_t` space -- plus an extra bit to detect potential rounding.
     33  constexpr uint8_t BitsNeededForShiftedMantissa = SignificandWidth + 1;
     34 
     35  uint64_t shiftedMantissa = 0;
     36  uint64_t exponent = 0;
     37 
     38  // If the extra bit is set, correctly rounding the result may require
     39  // examining all lower-order bits. Also compute 1) the index of the Digit
     40  // storing the extra bit, and 2) whether bits beneath the extra bit in that
     41  // Digit are nonzero so we can round if needed.
     42  uint64_t bitsBeneathExtraBitInDigitContainingExtraBit = 0;
     43 
     44  if (x.high == 0) {
     45    uint64_t msd = x.low;
     46 
     47    // Fast path for the likely-common case of up to a uint64_t of magnitude not
     48    // exceeding integral precision in IEEE-754.
     49    if (msd <= MaxIntegralPrecisionDouble) {
     50      return negative ? -double(msd) : +double(msd);
     51    }
     52 
     53    const uint8_t msdLeadingZeroes = mozilla::CountLeadingZeroes64(msd);
     54    MOZ_ASSERT(msdLeadingZeroes <= 10,
     55               "leading zeroes is at most 10 when the fast path isn't taken");
     56 
     57    exponent = 64 - msdLeadingZeroes - 1;
     58 
     59    // Omit the most significant bit: the IEEE-754 format includes this bit
     60    // implicitly for all double-precision integers.
     61    const uint8_t msdIgnoredBits = msdLeadingZeroes + 1;
     62    MOZ_ASSERT(1 <= msdIgnoredBits && msdIgnoredBits <= 11);
     63 
     64    const uint8_t msdIncludedBits = 64 - msdIgnoredBits;
     65    MOZ_ASSERT(53 <= msdIncludedBits && msdIncludedBits <= 63);
     66    MOZ_ASSERT(msdIncludedBits >= BitsNeededForShiftedMantissa);
     67 
     68    // Munge the most significant bits of the number into proper
     69    // position in an IEEE-754 double and go to town.
     70 
     71    // Shift `msd`'s contributed bits upward to remove high-order zeroes and the
     72    // highest set bit (which is implicit in IEEE-754 integral values so must be
     73    // removed) and to add low-order zeroes.  (Lower-order garbage bits are
     74    // discarded when `shiftedMantissa` is converted to a real mantissa.)
     75    shiftedMantissa = msd << (64 - msdIncludedBits);
     76 
     77    // Add shifted bits to `shiftedMantissa` until we have a complete mantissa
     78    // and an extra bit.
     79    const uint8_t countOfBitsInDigitBelowExtraBit =
     80        64 - BitsNeededForShiftedMantissa - msdIgnoredBits;
     81    bitsBeneathExtraBitInDigitContainingExtraBit =
     82        msd & ((uint64_t(1) << countOfBitsInDigitBelowExtraBit) - 1);
     83  } else {
     84    uint64_t msd = x.high;
     85    uint64_t second = x.low;
     86 
     87    uint8_t msdLeadingZeroes = mozilla::CountLeadingZeroes64(msd);
     88 
     89    exponent = 2 * 64 - msdLeadingZeroes - 1;
     90 
     91    // Munge the most significant bits of the number into proper
     92    // position in an IEEE-754 double and go to town.
     93 
     94    // Omit the most significant bit: the IEEE-754 format includes this bit
     95    // implicitly for all double-precision integers.
     96    const uint8_t msdIgnoredBits = msdLeadingZeroes + 1;
     97    const uint8_t msdIncludedBits = 64 - msdIgnoredBits;
     98 
     99    // Shift `msd`'s contributed bits upward to remove high-order zeroes and the
    100    // highest set bit (which is implicit in IEEE-754 integral values so must be
    101    // removed) and to add low-order zeroes.  (Lower-order garbage bits are
    102    // discarded when `shiftedMantissa` is converted to a real mantissa.)
    103    shiftedMantissa = msdIncludedBits == 0 ? 0 : msd << (64 - msdIncludedBits);
    104 
    105    // Add shifted bits to `shiftedMantissa` until we have a complete mantissa
    106    // and an extra bit.
    107    if (msdIncludedBits >= BitsNeededForShiftedMantissa) {
    108      const uint8_t countOfBitsInDigitBelowExtraBit =
    109          64 - BitsNeededForShiftedMantissa - msdIgnoredBits;
    110      bitsBeneathExtraBitInDigitContainingExtraBit =
    111          msd & ((uint64_t(1) << countOfBitsInDigitBelowExtraBit) - 1);
    112 
    113      if (bitsBeneathExtraBitInDigitContainingExtraBit == 0) {
    114        bitsBeneathExtraBitInDigitContainingExtraBit = second;
    115      }
    116    } else {
    117      shiftedMantissa |= second >> msdIncludedBits;
    118 
    119      const uint8_t countOfBitsInSecondDigitBelowExtraBit =
    120          (msdIncludedBits + 64) - BitsNeededForShiftedMantissa;
    121      bitsBeneathExtraBitInDigitContainingExtraBit =
    122          second << (64 - countOfBitsInSecondDigitBelowExtraBit);
    123    }
    124  }
    125 
    126  constexpr uint64_t LeastSignificantBit = uint64_t(1)
    127                                           << (64 - SignificandWidth);
    128  constexpr uint64_t ExtraBit = LeastSignificantBit >> 1;
    129 
    130  // The extra bit must be set for rounding to change the mantissa.
    131  if ((shiftedMantissa & ExtraBit) != 0) {
    132    bool shouldRoundUp;
    133    if (shiftedMantissa & LeastSignificantBit) {
    134      // If the lowest mantissa bit is set, it doesn't matter what lower bits
    135      // are: nearest-even rounds up regardless.
    136      shouldRoundUp = true;
    137    } else {
    138      // If the lowest mantissa bit is unset, *all* lower bits are relevant.
    139      // All-zero bits below the extra bit situates `x` halfway between two
    140      // values, and the nearest *even* value lies downward.  But if any bit
    141      // below the extra bit is set, `x` is closer to the rounded-up value.
    142      shouldRoundUp = bitsBeneathExtraBitInDigitContainingExtraBit != 0;
    143    }
    144 
    145    if (shouldRoundUp) {
    146      // Add one to the significand bits.  If they overflow, the exponent must
    147      // also be increased.  If *that* overflows, return the correct infinity.
    148      uint64_t before = shiftedMantissa;
    149      shiftedMantissa += ExtraBit;
    150      if (shiftedMantissa < before) {
    151        exponent++;
    152      }
    153    }
    154  }
    155 
    156  uint64_t significandBits = shiftedMantissa >> (64 - SignificandWidth);
    157  uint64_t signBit = uint64_t(negative ? 1 : 0) << SignShift;
    158  uint64_t exponentBits = (exponent + ExponentBias) << ExponentShift;
    159  return mozilla::BitwiseCast<double>(signBit | exponentBits | significandBits);
    160 }