| /* |
| * 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 <algorithm> |
| #include <float.h> |
| |
| #if defined(STARBOARD) |
| #include "starboard/double.h" |
| #endif |
| |
| namespace WebCore { |
| |
| namespace DecimalPrivate { |
| |
| static int const ExponentMax = 1023; |
| static int const ExponentMin = -1023; |
| static int const Precision = 18; |
| |
| static const uint64_t MaxCoefficient = UINT64_C(0x16345785D89FFFF); // 999999999999999999 == 18 9's |
| |
| // This class handles Decimal special values. |
| class SpecialValueHandler { |
| 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)); } |
| bool isZero() const { return !m_low && !m_high; } |
| 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; |
| while (result < MaxCoefficient / 100) { |
| while (remainder < divisor) { |
| remainder *= 10; |
| result *= 10; |
| --resultExponent; |
| } |
| result += remainder / divisor; |
| 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; |
| } |
| |
| // Round toward positive infinity. |
| // Note: Mac ports defines ceil(x) as wtf_ceil(x), so we can't use name "ceil" here. |
| Decimal Decimal::ceiling() const |
| { |
| if (isSpecial()) |
| return *this; |
| |
| if (exponent() >= 0) |
| return *this; |
| |
| uint64_t coefficient = m_data.coefficient(); |
| const int numberOfDigits = countDigits(coefficient); |
| const int numberOfDropDigits = -exponent(); |
| if (numberOfDigits < numberOfDropDigits) |
| return isPositive() ? Decimal(1) : zero(Positive); |
| |
| uint64_t result = scaleDown(coefficient, numberOfDropDigits); |
| uint64_t droppedDigits = coefficient - scaleUp(result, numberOfDropDigits); |
| if (droppedDigits && isPositive()) |
| result += 1; |
| 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 coefficient = m_data.coefficient(); |
| const int numberOfDigits = countDigits(coefficient); |
| const int numberOfDropDigits = -exponent(); |
| if (numberOfDigits < numberOfDropDigits) |
| return isPositive() ? zero(Positive) : Decimal(-1); |
| |
| uint64_t result = scaleDown(coefficient, numberOfDropDigits); |
| uint64_t droppedDigits = coefficient - scaleUp(result, numberOfDropDigits); |
| if (droppedDigits && isNegative()) { |
| result += 1; |
| } |
| return Decimal(sign(), 0, result); |
| } |
| |
| Decimal Decimal::fromDouble(double doubleValue) |
| { |
| #if defined(STARBOARD) |
| if (SbDoubleIsFinite(doubleValue)) |
| return fromString(mozToString(doubleValue)); |
| if (SbDoubleIsNan(doubleValue)) |
| return nan(); |
| return infinity(doubleValue < 0 ? Negative : Positive); |
| #else |
| if (std::isfinite(doubleValue)) |
| return fromString(mozToString(doubleValue)); |
| if (std::isinf(doubleValue)) |
| return infinity(doubleValue < 0 ? Negative : Positive); |
| return nan(); |
| #endif |
| } |
| |
| 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: |
| if (ch >= '0' && ch <= '9') { |
| if (numberOfDigits < Precision) { |
| ++numberOfDigits; |
| ++numberOfDigitsAfterDot; |
| accumulator *= 10; |
| accumulator += ch - '0'; |
| } |
| state = StateDotDigit; |
| break; |
| } |
| |
| case StateDotDigit: |
| if (ch >= '0' && ch <= '9') { |
| if (numberOfDigits < Precision) { |
| ++numberOfDigits; |
| ++numberOfDigitsAfterDot; |
| accumulator *= 10; |
| accumulator += ch - '0'; |
| } |
| 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.ceiling() : 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); |
| |
| // We're implementing round-half-away-from-zero, so we only need the one |
| // (the most significant) fractional digit: |
| 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 WebCore |
| |