diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /mozglue/misc/decimal/Decimal.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mozglue/misc/decimal/Decimal.cpp')
-rw-r--r-- | mozglue/misc/decimal/Decimal.cpp | 1063 |
1 files changed, 1063 insertions, 0 deletions
diff --git a/mozglue/misc/decimal/Decimal.cpp b/mozglue/misc/decimal/Decimal.cpp new file mode 100644 index 0000000000..cc828e2843 --- /dev/null +++ b/mozglue/misc/decimal/Decimal.cpp @@ -0,0 +1,1063 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Decimal.h" +#include "moz-decimal-utils.h" +#include "DoubleConversion.h" + +using namespace moz_decimal_utils; + +#include <algorithm> +#include <float.h> + +namespace blink { + +namespace DecimalPrivate { + +static int const ExponentMax = 1023; +static int const ExponentMin = -1023; +static int const Precision = 18; + +static const uint64_t MaxCoefficient = UINT64_C(0xDE0B6B3A763FFFF); // 999999999999999999 == 18 9's + +// This class handles Decimal special values. +class SpecialValueHandler { + STACK_ALLOCATED(); + WTF_MAKE_NONCOPYABLE(SpecialValueHandler); +public: + enum HandleResult { + BothFinite, + BothInfinity, + EitherNaN, + LHSIsInfinity, + RHSIsInfinity, + }; + + SpecialValueHandler(const Decimal& lhs, const Decimal& rhs); + HandleResult handle(); + Decimal value() const; + +private: + enum Result { + ResultIsLHS, + ResultIsRHS, + ResultIsUnknown, + }; + + const Decimal& m_lhs; + const Decimal& m_rhs; + Result m_result; +}; + +SpecialValueHandler::SpecialValueHandler(const Decimal& lhs, const Decimal& rhs) + : m_lhs(lhs), m_rhs(rhs), m_result(ResultIsUnknown) +{ +} + +SpecialValueHandler::HandleResult SpecialValueHandler::handle() +{ + if (m_lhs.isFinite() && m_rhs.isFinite()) + return BothFinite; + + const Decimal::EncodedData::FormatClass lhsClass = m_lhs.value().formatClass(); + const Decimal::EncodedData::FormatClass rhsClass = m_rhs.value().formatClass(); + if (lhsClass == Decimal::EncodedData::ClassNaN) { + m_result = ResultIsLHS; + return EitherNaN; + } + + if (rhsClass == Decimal::EncodedData::ClassNaN) { + m_result = ResultIsRHS; + return EitherNaN; + } + + if (lhsClass == Decimal::EncodedData::ClassInfinity) + return rhsClass == Decimal::EncodedData::ClassInfinity ? BothInfinity : LHSIsInfinity; + + if (rhsClass == Decimal::EncodedData::ClassInfinity) + return RHSIsInfinity; + + ASSERT_NOT_REACHED(); + return BothFinite; +} + +Decimal SpecialValueHandler::value() const +{ + switch (m_result) { + case ResultIsLHS: + return m_lhs; + case ResultIsRHS: + return m_rhs; + case ResultIsUnknown: + default: + ASSERT_NOT_REACHED(); + return m_lhs; + } +} + +// This class is used for 128 bit unsigned integer arithmetic. +class UInt128 { +public: + UInt128(uint64_t low, uint64_t high) + : m_high(high), m_low(low) + { + } + + UInt128& operator/=(uint32_t); + + uint64_t high() const { return m_high; } + uint64_t low() const { return m_low; } + + static UInt128 multiply(uint64_t u, uint64_t v) { return UInt128(u * v, multiplyHigh(u, v)); } + +private: + static uint32_t highUInt32(uint64_t x) { return static_cast<uint32_t>(x >> 32); } + static uint32_t lowUInt32(uint64_t x) { return static_cast<uint32_t>(x & ((static_cast<uint64_t>(1) << 32) - 1)); } + static uint64_t makeUInt64(uint32_t low, uint32_t high) { return low | (static_cast<uint64_t>(high) << 32); } + + static uint64_t multiplyHigh(uint64_t, uint64_t); + + uint64_t m_high; + uint64_t m_low; +}; + +UInt128& UInt128::operator/=(const uint32_t divisor) +{ + ASSERT(divisor); + + if (!m_high) { + m_low /= divisor; + return *this; + } + + uint32_t dividend[4]; + dividend[0] = lowUInt32(m_low); + dividend[1] = highUInt32(m_low); + dividend[2] = lowUInt32(m_high); + dividend[3] = highUInt32(m_high); + + uint32_t quotient[4]; + uint32_t remainder = 0; + for (int i = 3; i >= 0; --i) { + const uint64_t work = makeUInt64(dividend[i], remainder); + remainder = static_cast<uint32_t>(work % divisor); + quotient[i] = static_cast<uint32_t>(work / divisor); + } + m_low = makeUInt64(quotient[0], quotient[1]); + m_high = makeUInt64(quotient[2], quotient[3]); + return *this; +} + +// Returns high 64bit of 128bit product. +uint64_t UInt128::multiplyHigh(uint64_t u, uint64_t v) +{ + const uint64_t uLow = lowUInt32(u); + const uint64_t uHigh = highUInt32(u); + const uint64_t vLow = lowUInt32(v); + const uint64_t vHigh = highUInt32(v); + const uint64_t partialProduct = uHigh * vLow + highUInt32(uLow * vLow); + return uHigh * vHigh + highUInt32(partialProduct) + highUInt32(uLow * vHigh + lowUInt32(partialProduct)); +} + +static int countDigits(uint64_t x) +{ + int numberOfDigits = 0; + for (uint64_t powerOfTen = 1; x >= powerOfTen; powerOfTen *= 10) { + ++numberOfDigits; + if (powerOfTen >= std::numeric_limits<uint64_t>::max() / 10) + break; + } + return numberOfDigits; +} + +static uint64_t scaleDown(uint64_t x, int n) +{ + ASSERT(n >= 0); + while (n > 0 && x) { + x /= 10; + --n; + } + return x; +} + +static uint64_t scaleUp(uint64_t x, int n) +{ + ASSERT(n >= 0); + ASSERT(n <= Precision); + + uint64_t y = 1; + uint64_t z = 10; + for (;;) { + if (n & 1) + y = y * z; + + n >>= 1; + if (!n) + return x * y; + + z = z * z; + } +} + +} // namespace DecimalPrivate + +using namespace DecimalPrivate; + +Decimal::EncodedData::EncodedData(Sign sign, FormatClass formatClass) + : m_coefficient(0) + , m_exponent(0) + , m_formatClass(formatClass) + , m_sign(sign) +{ +} + +Decimal::EncodedData::EncodedData(Sign sign, int exponent, uint64_t coefficient) + : m_formatClass(coefficient ? ClassNormal : ClassZero) + , m_sign(sign) +{ + if (exponent >= ExponentMin && exponent <= ExponentMax) { + while (coefficient > MaxCoefficient) { + coefficient /= 10; + ++exponent; + } + } + + if (exponent > ExponentMax) { + m_coefficient = 0; + m_exponent = 0; + m_formatClass = ClassInfinity; + return; + } + + if (exponent < ExponentMin) { + m_coefficient = 0; + m_exponent = 0; + m_formatClass = ClassZero; + return; + } + + m_coefficient = coefficient; + m_exponent = static_cast<int16_t>(exponent); +} + +bool Decimal::EncodedData::operator==(const EncodedData& another) const +{ + return m_sign == another.m_sign + && m_formatClass == another.m_formatClass + && m_exponent == another.m_exponent + && m_coefficient == another.m_coefficient; +} + +Decimal::Decimal(int32_t i32) + : m_data(i32 < 0 ? Negative : Positive, 0, i32 < 0 ? static_cast<uint64_t>(-static_cast<int64_t>(i32)) : static_cast<uint64_t>(i32)) +{ +} + +Decimal::Decimal(Sign sign, int exponent, uint64_t coefficient) + : m_data(sign, coefficient ? exponent : 0, coefficient) +{ +} + +Decimal::Decimal(const EncodedData& data) + : m_data(data) +{ +} + +Decimal::Decimal(const Decimal& other) + : m_data(other.m_data) +{ +} + +Decimal& Decimal::operator=(const Decimal& other) +{ + m_data = other.m_data; + return *this; +} + +Decimal& Decimal::operator+=(const Decimal& other) +{ + m_data = (*this + other).m_data; + return *this; +} + +Decimal& Decimal::operator-=(const Decimal& other) +{ + m_data = (*this - other).m_data; + return *this; +} + +Decimal& Decimal::operator*=(const Decimal& other) +{ + m_data = (*this * other).m_data; + return *this; +} + +Decimal& Decimal::operator/=(const Decimal& other) +{ + m_data = (*this / other).m_data; + return *this; +} + +Decimal Decimal::operator-() const +{ + if (isNaN()) + return *this; + + Decimal result(*this); + result.m_data.setSign(invertSign(m_data.sign())); + return result; +} + +Decimal Decimal::operator+(const Decimal& rhs) const +{ + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); + + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { + case SpecialValueHandler::BothFinite: + break; + + case SpecialValueHandler::BothInfinity: + return lhsSign == rhsSign ? lhs : nan(); + + case SpecialValueHandler::EitherNaN: + return handler.value(); + + case SpecialValueHandler::LHSIsInfinity: + return lhs; + + case SpecialValueHandler::RHSIsInfinity: + return rhs; + } + + const AlignedOperands alignedOperands = alignOperands(lhs, rhs); + + const uint64_t result = lhsSign == rhsSign + ? alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient + : alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient; + + if (lhsSign == Negative && rhsSign == Positive && !result) + return Decimal(Positive, alignedOperands.exponent, 0); + + return static_cast<int64_t>(result) >= 0 + ? Decimal(lhsSign, alignedOperands.exponent, result) + : Decimal(invertSign(lhsSign), alignedOperands.exponent, -static_cast<int64_t>(result)); +} + +Decimal Decimal::operator-(const Decimal& rhs) const +{ + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); + + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { + case SpecialValueHandler::BothFinite: + break; + + case SpecialValueHandler::BothInfinity: + return lhsSign == rhsSign ? nan() : lhs; + + case SpecialValueHandler::EitherNaN: + return handler.value(); + + case SpecialValueHandler::LHSIsInfinity: + return lhs; + + case SpecialValueHandler::RHSIsInfinity: + return infinity(invertSign(rhsSign)); + } + + const AlignedOperands alignedOperands = alignOperands(lhs, rhs); + + const uint64_t result = lhsSign == rhsSign + ? alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient + : alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient; + + if (lhsSign == Negative && rhsSign == Negative && !result) + return Decimal(Positive, alignedOperands.exponent, 0); + + return static_cast<int64_t>(result) >= 0 + ? Decimal(lhsSign, alignedOperands.exponent, result) + : Decimal(invertSign(lhsSign), alignedOperands.exponent, -static_cast<int64_t>(result)); +} + +Decimal Decimal::operator*(const Decimal& rhs) const +{ + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); + const Sign resultSign = lhsSign == rhsSign ? Positive : Negative; + + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { + case SpecialValueHandler::BothFinite: { + const uint64_t lhsCoefficient = lhs.m_data.coefficient(); + const uint64_t rhsCoefficient = rhs.m_data.coefficient(); + int resultExponent = lhs.exponent() + rhs.exponent(); + UInt128 work(UInt128::multiply(lhsCoefficient, rhsCoefficient)); + while (work.high()) { + work /= 10; + ++resultExponent; + } + return Decimal(resultSign, resultExponent, work.low()); + } + + case SpecialValueHandler::BothInfinity: + return infinity(resultSign); + + case SpecialValueHandler::EitherNaN: + return handler.value(); + + case SpecialValueHandler::LHSIsInfinity: + return rhs.isZero() ? nan() : infinity(resultSign); + + case SpecialValueHandler::RHSIsInfinity: + return lhs.isZero() ? nan() : infinity(resultSign); + } + + ASSERT_NOT_REACHED(); + return nan(); +} + +Decimal Decimal::operator/(const Decimal& rhs) const +{ + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); + const Sign resultSign = lhsSign == rhsSign ? Positive : Negative; + + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { + case SpecialValueHandler::BothFinite: + break; + + case SpecialValueHandler::BothInfinity: + return nan(); + + case SpecialValueHandler::EitherNaN: + return handler.value(); + + case SpecialValueHandler::LHSIsInfinity: + return infinity(resultSign); + + case SpecialValueHandler::RHSIsInfinity: + return zero(resultSign); + } + + ASSERT(lhs.isFinite()); + ASSERT(rhs.isFinite()); + + if (rhs.isZero()) + return lhs.isZero() ? nan() : infinity(resultSign); + + int resultExponent = lhs.exponent() - rhs.exponent(); + + if (lhs.isZero()) + return Decimal(resultSign, resultExponent, 0); + + uint64_t remainder = lhs.m_data.coefficient(); + const uint64_t divisor = rhs.m_data.coefficient(); + uint64_t result = 0; + for (;;) { + while (remainder < divisor && result < MaxCoefficient / 10) { + remainder *= 10; + result *= 10; + --resultExponent; + } + if (remainder < divisor) + break; + uint64_t quotient = remainder / divisor; + if (result > MaxCoefficient - quotient) + break; + result += quotient; + remainder %= divisor; + if (!remainder) + break; + } + + if (remainder > divisor / 2) + ++result; + + return Decimal(resultSign, resultExponent, result); +} + +bool Decimal::operator==(const Decimal& rhs) const +{ + if (isNaN() || rhs.isNaN()) + return false; + return m_data == rhs.m_data || compareTo(rhs).isZero(); +} + +bool Decimal::operator!=(const Decimal& rhs) const +{ + if (isNaN() || rhs.isNaN()) + return true; + if (m_data == rhs.m_data) + return false; + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return !result.isZero(); +} + +bool Decimal::operator<(const Decimal& rhs) const +{ + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return !result.isZero() && result.isNegative(); +} + +bool Decimal::operator<=(const Decimal& rhs) const +{ + if (isNaN() || rhs.isNaN()) + return false; + if (m_data == rhs.m_data) + return true; + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return result.isZero() || result.isNegative(); +} + +bool Decimal::operator>(const Decimal& rhs) const +{ + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return !result.isZero() && result.isPositive(); +} + +bool Decimal::operator>=(const Decimal& rhs) const +{ + if (isNaN() || rhs.isNaN()) + return false; + if (m_data == rhs.m_data) + return true; + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return result.isZero() || !result.isNegative(); +} + +Decimal Decimal::abs() const +{ + Decimal result(*this); + result.m_data.setSign(Positive); + return result; +} + +Decimal::AlignedOperands Decimal::alignOperands(const Decimal& lhs, const Decimal& rhs) +{ + ASSERT(lhs.isFinite()); + ASSERT(rhs.isFinite()); + + const int lhsExponent = lhs.exponent(); + const int rhsExponent = rhs.exponent(); + int exponent = std::min(lhsExponent, rhsExponent); + uint64_t lhsCoefficient = lhs.m_data.coefficient(); + uint64_t rhsCoefficient = rhs.m_data.coefficient(); + + if (lhsExponent > rhsExponent) { + const int numberOfLHSDigits = countDigits(lhsCoefficient); + if (numberOfLHSDigits) { + const int lhsShiftAmount = lhsExponent - rhsExponent; + const int overflow = numberOfLHSDigits + lhsShiftAmount - Precision; + if (overflow <= 0) { + lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount); + } else { + lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount - overflow); + rhsCoefficient = scaleDown(rhsCoefficient, overflow); + exponent += overflow; + } + } + + } else if (lhsExponent < rhsExponent) { + const int numberOfRHSDigits = countDigits(rhsCoefficient); + if (numberOfRHSDigits) { + const int rhsShiftAmount = rhsExponent - lhsExponent; + const int overflow = numberOfRHSDigits + rhsShiftAmount - Precision; + if (overflow <= 0) { + rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount); + } else { + rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount - overflow); + lhsCoefficient = scaleDown(lhsCoefficient, overflow); + exponent += overflow; + } + } + } + + AlignedOperands alignedOperands; + alignedOperands.exponent = exponent; + alignedOperands.lhsCoefficient = lhsCoefficient; + alignedOperands.rhsCoefficient = rhsCoefficient; + return alignedOperands; +} + +static bool isMultiplePowersOfTen(uint64_t coefficient, int n) +{ + return !coefficient || !(coefficient % scaleUp(1, n)); +} + +// Round toward positive infinity. +Decimal Decimal::ceil() const +{ + if (isSpecial()) + return *this; + + if (exponent() >= 0) + return *this; + + uint64_t result = m_data.coefficient(); + const int numberOfDigits = countDigits(result); + const int numberOfDropDigits = -exponent(); + if (numberOfDigits <= numberOfDropDigits) + return isPositive() ? Decimal(1) : zero(Positive); + + result = scaleDown(result, numberOfDropDigits); + if (isPositive() && !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits)) + ++result; + return Decimal(sign(), 0, result); +} + +Decimal Decimal::compareTo(const Decimal& rhs) const +{ + const Decimal result(*this - rhs); + switch (result.m_data.formatClass()) { + case EncodedData::ClassInfinity: + return result.isNegative() ? Decimal(-1) : Decimal(1); + + case EncodedData::ClassNaN: + case EncodedData::ClassNormal: + return result; + + case EncodedData::ClassZero: + return zero(Positive); + + default: + ASSERT_NOT_REACHED(); + return nan(); + } +} + +// Round toward negative infinity. +Decimal Decimal::floor() const +{ + if (isSpecial()) + return *this; + + if (exponent() >= 0) + return *this; + + uint64_t result = m_data.coefficient(); + const int numberOfDigits = countDigits(result); + const int numberOfDropDigits = -exponent(); + if (numberOfDigits < numberOfDropDigits) + return isPositive() ? zero(Positive) : Decimal(-1); + + result = scaleDown(result, numberOfDropDigits); + if (isNegative() && !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits)) + ++result; + return Decimal(sign(), 0, result); +} + +Decimal Decimal::fromDouble(double doubleValue) +{ + if (std::isfinite(doubleValue)) + return fromString(mozToString(doubleValue)); + + if (std::isinf(doubleValue)) + return infinity(doubleValue < 0 ? Negative : Positive); + + return nan(); +} + +Decimal Decimal::fromString(const String& str) +{ + int exponent = 0; + Sign exponentSign = Positive; + int numberOfDigits = 0; + int numberOfDigitsAfterDot = 0; + int numberOfExtraDigits = 0; + Sign sign = Positive; + + enum { + StateDigit, + StateDot, + StateDotDigit, + StateE, + StateEDigit, + StateESign, + StateSign, + StateStart, + StateZero, + } state = StateStart; + +#define HandleCharAndBreak(expected, nextState) \ + if (ch == expected) { \ + state = nextState; \ + break; \ + } + +#define HandleTwoCharsAndBreak(expected1, expected2, nextState) \ + if (ch == expected1 || ch == expected2) { \ + state = nextState; \ + break; \ + } + + uint64_t accumulator = 0; + for (unsigned index = 0; index < str.length(); ++index) { + const int ch = str[index]; + switch (state) { + case StateDigit: + if (ch >= '0' && ch <= '9') { + if (numberOfDigits < Precision) { + ++numberOfDigits; + accumulator *= 10; + accumulator += ch - '0'; + } else { + ++numberOfExtraDigits; + } + break; + } + + HandleCharAndBreak('.', StateDot); + HandleTwoCharsAndBreak('E', 'e', StateE); + return nan(); + + case StateDot: + case StateDotDigit: + if (ch >= '0' && ch <= '9') { + if (numberOfDigits < Precision) { + ++numberOfDigits; + ++numberOfDigitsAfterDot; + accumulator *= 10; + accumulator += ch - '0'; + } + state = StateDotDigit; + break; + } + + HandleTwoCharsAndBreak('E', 'e', StateE); + return nan(); + + case StateE: + if (ch == '+') { + exponentSign = Positive; + state = StateESign; + break; + } + + if (ch == '-') { + exponentSign = Negative; + state = StateESign; + break; + } + + if (ch >= '0' && ch <= '9') { + exponent = ch - '0'; + state = StateEDigit; + break; + } + + return nan(); + + case StateEDigit: + if (ch >= '0' && ch <= '9') { + exponent *= 10; + exponent += ch - '0'; + if (exponent > ExponentMax + Precision) { + if (accumulator) + return exponentSign == Negative ? zero(Positive) : infinity(sign); + return zero(sign); + } + state = StateEDigit; + break; + } + + return nan(); + + case StateESign: + if (ch >= '0' && ch <= '9') { + exponent = ch - '0'; + state = StateEDigit; + break; + } + + return nan(); + + case StateSign: + if (ch >= '1' && ch <= '9') { + accumulator = ch - '0'; + numberOfDigits = 1; + state = StateDigit; + break; + } + + HandleCharAndBreak('0', StateZero); + return nan(); + + case StateStart: + if (ch >= '1' && ch <= '9') { + accumulator = ch - '0'; + numberOfDigits = 1; + state = StateDigit; + break; + } + + if (ch == '-') { + sign = Negative; + state = StateSign; + break; + } + + if (ch == '+') { + sign = Positive; + state = StateSign; + break; + } + + HandleCharAndBreak('0', StateZero); + HandleCharAndBreak('.', StateDot); + return nan(); + + case StateZero: + if (ch == '0') + break; + + if (ch >= '1' && ch <= '9') { + accumulator = ch - '0'; + numberOfDigits = 1; + state = StateDigit; + break; + } + + HandleCharAndBreak('.', StateDot); + HandleTwoCharsAndBreak('E', 'e', StateE); + return nan(); + + default: + ASSERT_NOT_REACHED(); + return nan(); + } + } + + if (state == StateZero) + return zero(sign); + + if (state == StateDigit || state == StateEDigit || state == StateDotDigit) { + int resultExponent = exponent * (exponentSign == Negative ? -1 : 1) - numberOfDigitsAfterDot + numberOfExtraDigits; + if (resultExponent < ExponentMin) + return zero(Positive); + + const int overflow = resultExponent - ExponentMax + 1; + if (overflow > 0) { + if (overflow + numberOfDigits - numberOfDigitsAfterDot > Precision) + return infinity(sign); + accumulator = scaleUp(accumulator, overflow); + resultExponent -= overflow; + } + + return Decimal(sign, resultExponent, accumulator); + } + + return nan(); +} + +Decimal Decimal::infinity(const Sign sign) +{ + return Decimal(EncodedData(sign, EncodedData::ClassInfinity)); +} + +Decimal Decimal::nan() +{ + return Decimal(EncodedData(Positive, EncodedData::ClassNaN)); +} + +Decimal Decimal::remainder(const Decimal& rhs) const +{ + const Decimal quotient = *this / rhs; + return quotient.isSpecial() ? quotient : *this - (quotient.isNegative() ? quotient.ceil() : quotient.floor()) * rhs; +} + +Decimal Decimal::round() const +{ + if (isSpecial()) + return *this; + + if (exponent() >= 0) + return *this; + + uint64_t result = m_data.coefficient(); + const int numberOfDigits = countDigits(result); + const int numberOfDropDigits = -exponent(); + if (numberOfDigits < numberOfDropDigits) + return zero(Positive); + + result = scaleDown(result, numberOfDropDigits - 1); + if (result % 10 >= 5) + result += 10; + result /= 10; + return Decimal(sign(), 0, result); +} + +double Decimal::toDouble() const +{ + if (isFinite()) { + bool valid; + const double doubleValue = mozToDouble(toString(), &valid); + return valid ? doubleValue : std::numeric_limits<double>::quiet_NaN(); + } + + if (isInfinity()) + return isNegative() ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity(); + + return std::numeric_limits<double>::quiet_NaN(); +} + +String Decimal::toString() const +{ + switch (m_data.formatClass()) { + case EncodedData::ClassInfinity: + return sign() ? "-Infinity" : "Infinity"; + + case EncodedData::ClassNaN: + return "NaN"; + + case EncodedData::ClassNormal: + case EncodedData::ClassZero: + break; + + default: + ASSERT_NOT_REACHED(); + return ""; + } + + StringBuilder builder; + if (sign()) + builder.append('-'); + + int originalExponent = exponent(); + uint64_t coefficient = m_data.coefficient(); + + if (originalExponent < 0) { + const int maxDigits = DBL_DIG; + uint64_t lastDigit = 0; + while (countDigits(coefficient) > maxDigits) { + lastDigit = coefficient % 10; + coefficient /= 10; + ++originalExponent; + } + + if (lastDigit >= 5) + ++coefficient; + + while (originalExponent < 0 && coefficient && !(coefficient % 10)) { + coefficient /= 10; + ++originalExponent; + } + } + + const String digits = mozToString(coefficient); + int coefficientLength = static_cast<int>(digits.length()); + const int adjustedExponent = originalExponent + coefficientLength - 1; + if (originalExponent <= 0 && adjustedExponent >= -6) { + if (!originalExponent) { + builder.append(digits); + return builder.toString(); + } + + if (adjustedExponent >= 0) { + for (int i = 0; i < coefficientLength; ++i) { + builder.append(digits[i]); + if (i == adjustedExponent) + builder.append('.'); + } + return builder.toString(); + } + + builder.appendLiteral("0."); + for (int i = adjustedExponent + 1; i < 0; ++i) + builder.append('0'); + + builder.append(digits); + + } else { + builder.append(digits[0]); + while (coefficientLength >= 2 && digits[coefficientLength - 1] == '0') + --coefficientLength; + if (coefficientLength >= 2) { + builder.append('.'); + for (int i = 1; i < coefficientLength; ++i) + builder.append(digits[i]); + } + + if (adjustedExponent) { + builder.append(adjustedExponent < 0 ? "e" : "e+"); + builder.appendNumber(adjustedExponent); + } + } + return builder.toString(); +} + +bool Decimal::toString(char* strBuf, size_t bufLength) const +{ + ASSERT(bufLength > 0); + String str = toString(); + size_t length = str.copy(strBuf, bufLength); + if (length < bufLength) { + strBuf[length] = '\0'; + return true; + } + strBuf[bufLength - 1] = '\0'; + return false; +} + +Decimal Decimal::zero(Sign sign) +{ + return Decimal(EncodedData(sign, EncodedData::ClassZero)); +} + +} // namespace blink + +// Implementation of DoubleConversion.h: + +namespace mozilla { + +Maybe<double> StringToDouble(Span<const char> aStringSpan) { + bool valid = false; + double result = mozToDouble(aStringSpan, &valid); + return valid ? Some(result) : Nothing(); +} + +} |