diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /js/src/builtin/temporal/Int128.cpp | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/builtin/temporal/Int128.cpp')
-rw-r--r-- | js/src/builtin/temporal/Int128.cpp | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/js/src/builtin/temporal/Int128.cpp b/js/src/builtin/temporal/Int128.cpp new file mode 100644 index 0000000000..e712a64be6 --- /dev/null +++ b/js/src/builtin/temporal/Int128.cpp @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 "builtin/temporal/Int128.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Casting.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/MathAlgorithms.h" + +#include <stdint.h> + +using namespace js; +using namespace js::temporal; + +double Uint128::toDouble(const Uint128& x, bool negative) { + // Simplified version of |BigInt::numberValue()| for DigitBits=64. See the + // comments in |BigInt::numberValue()| for how this code works. + + using Double = mozilla::FloatingPoint<double>; + constexpr uint8_t ExponentShift = Double::kExponentShift; + constexpr uint8_t SignificandWidth = Double::kSignificandWidth; + constexpr unsigned ExponentBias = Double::kExponentBias; + constexpr uint8_t SignShift = Double::kExponentWidth + SignificandWidth; + + constexpr uint64_t MaxIntegralPrecisionDouble = uint64_t(1) + << (SignificandWidth + 1); + + // 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; + + uint64_t shiftedMantissa = 0; + uint64_t exponent = 0; + + // 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. + uint64_t bitsBeneathExtraBitInDigitContainingExtraBit = 0; + + if (x.high == 0) { + uint64_t msd = x.low; + + // Fast path for the likely-common case of up to a uint64_t of magnitude not + // exceeding integral precision in IEEE-754. + if (msd <= MaxIntegralPrecisionDouble) { + return negative ? -double(msd) : +double(msd); + } + + const uint8_t msdLeadingZeroes = mozilla::CountLeadingZeroes64(msd); + MOZ_ASSERT(0 <= msdLeadingZeroes && msdLeadingZeroes <= 10, + "leading zeroes is at most 10 when the fast path isn't taken"); + + exponent = 64 - msdLeadingZeroes - 1; + + // Omit the most significant bit: the IEEE-754 format includes this bit + // implicitly for all double-precision integers. + const uint8_t msdIgnoredBits = msdLeadingZeroes + 1; + MOZ_ASSERT(1 <= msdIgnoredBits && msdIgnoredBits <= 11); + + const uint8_t msdIncludedBits = 64 - msdIgnoredBits; + MOZ_ASSERT(53 <= msdIncludedBits && msdIncludedBits <= 63); + MOZ_ASSERT(msdIncludedBits >= BitsNeededForShiftedMantissa); + + // Munge the most significant bits of the number into proper + // position in an IEEE-754 double and go to town. + + // 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.) + shiftedMantissa = msd << (64 - msdIncludedBits); + + // Add shifted bits to `shiftedMantissa` until we have a complete mantissa + // and an extra bit. + const uint8_t countOfBitsInDigitBelowExtraBit = + 64 - BitsNeededForShiftedMantissa - msdIgnoredBits; + bitsBeneathExtraBitInDigitContainingExtraBit = + msd & ((uint64_t(1) << countOfBitsInDigitBelowExtraBit) - 1); + } else { + uint64_t msd = x.high; + uint64_t second = x.low; + + uint8_t msdLeadingZeroes = mozilla::CountLeadingZeroes64(msd); + + exponent = 2 * 64 - msdLeadingZeroes - 1; + + // 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 = 64 - msdIgnoredBits; + + // 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.) + shiftedMantissa = msdIncludedBits == 0 ? 0 : msd << (64 - msdIncludedBits); + + // Add shifted bits to `shiftedMantissa` until we have a complete mantissa + // and an extra bit. + if (msdIncludedBits >= BitsNeededForShiftedMantissa) { + const uint8_t countOfBitsInDigitBelowExtraBit = + 64 - BitsNeededForShiftedMantissa - msdIgnoredBits; + bitsBeneathExtraBitInDigitContainingExtraBit = + msd & ((uint64_t(1) << countOfBitsInDigitBelowExtraBit) - 1); + + if (bitsBeneathExtraBitInDigitContainingExtraBit == 0) { + bitsBeneathExtraBitInDigitContainingExtraBit = second; + } + } else { + shiftedMantissa |= second >> msdIncludedBits; + + const uint8_t countOfBitsInSecondDigitBelowExtraBit = + (msdIncludedBits + 64) - BitsNeededForShiftedMantissa; + bitsBeneathExtraBitInDigitContainingExtraBit = + second << (64 - countOfBitsInSecondDigitBelowExtraBit); + } + } + + 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) { + // 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++; + } + } + } + + uint64_t significandBits = shiftedMantissa >> (64 - SignificandWidth); + uint64_t signBit = uint64_t(negative ? 1 : 0) << SignShift; + uint64_t exponentBits = (exponent + ExponentBias) << ExponentShift; + return mozilla::BitwiseCast<double>(signBit | exponentBits | significandBits); +} |