summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/decimal.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/decimal.cc')
-rw-r--r--chromium/third_party/blink/renderer/platform/decimal.cc1007
1 files changed, 1007 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/decimal.cc b/chromium/third_party/blink/renderer/platform/decimal.cc
new file mode 100644
index 00000000000..10979afd0d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/decimal.cc
@@ -0,0 +1,1007 @@
+/*
+ * 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 "third_party/blink/renderer/platform/decimal.h"
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#include <algorithm>
+#include <float.h>
+
+namespace blink {
+
+namespace DecimalPrivate {
+
+static int const kExponentMax = 1023;
+static int const kExponentMin = -1023;
+static int const kPrecision = 18;
+
+static const uint64_t kMaxCoefficient =
+ UINT64_C(0xDE0B6B3A763FFFF); // 999999999999999999 == 18 9's
+
+// This class handles Decimal special values.
+class SpecialValueHandler {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(SpecialValueHandler);
+
+ public:
+ enum HandleResult {
+ kBothFinite,
+ kBothInfinity,
+ kEitherNaN,
+ kLHSIsInfinity,
+ kRHSIsInfinity,
+ };
+
+ SpecialValueHandler(const Decimal& lhs, const Decimal& rhs);
+ HandleResult Handle();
+ Decimal Value() const;
+
+ private:
+ enum Result {
+ kResultIsLHS,
+ kResultIsRHS,
+ kResultIsUnknown,
+ };
+
+ const Decimal& lhs_;
+ const Decimal& rhs_;
+ Result result_;
+};
+
+SpecialValueHandler::SpecialValueHandler(const Decimal& lhs, const Decimal& rhs)
+ : lhs_(lhs), rhs_(rhs), result_(kResultIsUnknown) {}
+
+SpecialValueHandler::HandleResult SpecialValueHandler::Handle() {
+ if (lhs_.IsFinite() && rhs_.IsFinite())
+ return kBothFinite;
+
+ const Decimal::EncodedData::FormatClass lhs_class =
+ lhs_.Value().GetFormatClass();
+ const Decimal::EncodedData::FormatClass rhs_class =
+ rhs_.Value().GetFormatClass();
+ if (lhs_class == Decimal::EncodedData::kClassNaN) {
+ result_ = kResultIsLHS;
+ return kEitherNaN;
+ }
+
+ if (rhs_class == Decimal::EncodedData::kClassNaN) {
+ result_ = kResultIsRHS;
+ return kEitherNaN;
+ }
+
+ if (lhs_class == Decimal::EncodedData::kClassInfinity)
+ return rhs_class == Decimal::EncodedData::kClassInfinity ? kBothInfinity
+ : kLHSIsInfinity;
+
+ if (rhs_class == Decimal::EncodedData::kClassInfinity)
+ return kRHSIsInfinity;
+
+ NOTREACHED();
+ return kBothFinite;
+}
+
+Decimal SpecialValueHandler::Value() const {
+ switch (result_) {
+ case kResultIsLHS:
+ return lhs_;
+ case kResultIsRHS:
+ return rhs_;
+ case kResultIsUnknown:
+ default:
+ NOTREACHED();
+ return lhs_;
+ }
+}
+
+// This class is used for 128 bit unsigned integer arithmetic.
+class UInt128 {
+ public:
+ UInt128(uint64_t low, uint64_t high) : high_(high), low_(low) {}
+
+ UInt128& operator/=(uint32_t);
+
+ uint64_t High() const { return high_; }
+ uint64_t Low() const { return 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 high_;
+ uint64_t low_;
+};
+
+UInt128& UInt128::operator/=(const uint32_t divisor) {
+ DCHECK(divisor);
+
+ if (!high_) {
+ low_ /= divisor;
+ return *this;
+ }
+
+ uint32_t dividend[4];
+ dividend[0] = LowUInt32(low_);
+ dividend[1] = HighUInt32(low_);
+ dividend[2] = LowUInt32(high_);
+ dividend[3] = HighUInt32(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);
+ }
+ low_ = MakeUInt64(quotient[0], quotient[1]);
+ 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 u_low = LowUInt32(u);
+ const uint64_t u_high = HighUInt32(u);
+ const uint64_t v_low = LowUInt32(v);
+ const uint64_t v_high = HighUInt32(v);
+ const uint64_t partial_product = u_high * v_low + HighUInt32(u_low * v_low);
+ return u_high * v_high + HighUInt32(partial_product) +
+ HighUInt32(u_low * v_high + LowUInt32(partial_product));
+}
+
+static int CountDigits(uint64_t x) {
+ int number_of_digits = 0;
+ for (uint64_t power_of_ten = 1; x >= power_of_ten; power_of_ten *= 10) {
+ ++number_of_digits;
+ if (power_of_ten >= std::numeric_limits<uint64_t>::max() / 10)
+ break;
+ }
+ return number_of_digits;
+}
+
+static uint64_t ScaleDown(uint64_t x, int n) {
+ DCHECK_GE(n, 0);
+ while (n > 0 && x) {
+ x /= 10;
+ --n;
+ }
+ return x;
+}
+
+static uint64_t ScaleUp(uint64_t x, int n) {
+ DCHECK_GE(n, 0);
+ DCHECK_LE(n, kPrecision);
+
+ 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 format_class)
+ : coefficient_(0), exponent_(0), format_class_(format_class), sign_(sign) {}
+
+Decimal::EncodedData::EncodedData(Sign sign, int exponent, uint64_t coefficient)
+ : format_class_(coefficient ? kClassNormal : kClassZero), sign_(sign) {
+ if (exponent >= kExponentMin && exponent <= kExponentMax) {
+ while (coefficient > kMaxCoefficient) {
+ coefficient /= 10;
+ ++exponent;
+ }
+ }
+
+ if (exponent > kExponentMax) {
+ coefficient_ = 0;
+ exponent_ = 0;
+ format_class_ = kClassInfinity;
+ return;
+ }
+
+ if (exponent < kExponentMin) {
+ coefficient_ = 0;
+ exponent_ = 0;
+ format_class_ = kClassZero;
+ return;
+ }
+
+ coefficient_ = coefficient;
+ exponent_ = static_cast<int16_t>(exponent);
+}
+
+bool Decimal::EncodedData::operator==(const EncodedData& another) const {
+ return sign_ == another.sign_ && format_class_ == another.format_class_ &&
+ exponent_ == another.exponent_ && coefficient_ == another.coefficient_;
+}
+
+Decimal::Decimal(int32_t i32)
+ : data_(i32 < 0 ? kNegative : kPositive,
+ 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)
+ : data_(sign, exponent, coefficient) {}
+
+Decimal::Decimal(const EncodedData& data) : data_(data) {}
+
+Decimal::Decimal(const Decimal& other) = default;
+
+Decimal& Decimal::operator=(const Decimal& other) = default;
+
+Decimal& Decimal::operator+=(const Decimal& other) {
+ data_ = (*this + other).data_;
+ return *this;
+}
+
+Decimal& Decimal::operator-=(const Decimal& other) {
+ data_ = (*this - other).data_;
+ return *this;
+}
+
+Decimal& Decimal::operator*=(const Decimal& other) {
+ data_ = (*this * other).data_;
+ return *this;
+}
+
+Decimal& Decimal::operator/=(const Decimal& other) {
+ data_ = (*this / other).data_;
+ return *this;
+}
+
+Decimal Decimal::operator-() const {
+ if (IsNaN())
+ return *this;
+
+ Decimal result(*this);
+ result.data_.SetSign(InvertSign(data_.GetSign()));
+ return result;
+}
+
+Decimal Decimal::operator+(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite:
+ break;
+
+ case SpecialValueHandler::kBothInfinity:
+ return lhs_sign == rhs_sign ? lhs : Nan();
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return lhs;
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return rhs;
+ }
+
+ const AlignedOperands aligned_operands = AlignOperands(lhs, rhs);
+
+ const uint64_t result =
+ lhs_sign == rhs_sign
+ ? aligned_operands.lhs_coefficient + aligned_operands.rhs_coefficient
+ : aligned_operands.lhs_coefficient - aligned_operands.rhs_coefficient;
+
+ if (lhs_sign == kNegative && rhs_sign == kPositive && !result)
+ return Decimal(kPositive, aligned_operands.exponent, 0);
+
+ return static_cast<int64_t>(result) >= 0
+ ? Decimal(lhs_sign, aligned_operands.exponent, result)
+ : Decimal(InvertSign(lhs_sign), aligned_operands.exponent,
+ -static_cast<int64_t>(result));
+}
+
+Decimal Decimal::operator-(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite:
+ break;
+
+ case SpecialValueHandler::kBothInfinity:
+ return lhs_sign == rhs_sign ? Nan() : lhs;
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return lhs;
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return Infinity(InvertSign(rhs_sign));
+ }
+
+ const AlignedOperands aligned_operands = AlignOperands(lhs, rhs);
+
+ const uint64_t result =
+ lhs_sign == rhs_sign
+ ? aligned_operands.lhs_coefficient - aligned_operands.rhs_coefficient
+ : aligned_operands.lhs_coefficient + aligned_operands.rhs_coefficient;
+
+ if (lhs_sign == kNegative && rhs_sign == kNegative && !result)
+ return Decimal(kPositive, aligned_operands.exponent, 0);
+
+ return static_cast<int64_t>(result) >= 0
+ ? Decimal(lhs_sign, aligned_operands.exponent, result)
+ : Decimal(InvertSign(lhs_sign), aligned_operands.exponent,
+ -static_cast<int64_t>(result));
+}
+
+Decimal Decimal::operator*(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+ const Sign result_sign = lhs_sign == rhs_sign ? kPositive : kNegative;
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite: {
+ const uint64_t lhs_coefficient = lhs.data_.Coefficient();
+ const uint64_t rhs_coefficient = rhs.data_.Coefficient();
+ int result_exponent = lhs.Exponent() + rhs.Exponent();
+ UInt128 work(UInt128::Multiply(lhs_coefficient, rhs_coefficient));
+ while (work.High()) {
+ work /= 10;
+ ++result_exponent;
+ }
+ return Decimal(result_sign, result_exponent, work.Low());
+ }
+
+ case SpecialValueHandler::kBothInfinity:
+ return Infinity(result_sign);
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return rhs.IsZero() ? Nan() : Infinity(result_sign);
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return lhs.IsZero() ? Nan() : Infinity(result_sign);
+ }
+
+ NOTREACHED();
+ return Nan();
+}
+
+Decimal Decimal::operator/(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+ const Sign result_sign = lhs_sign == rhs_sign ? kPositive : kNegative;
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite:
+ break;
+
+ case SpecialValueHandler::kBothInfinity:
+ return Nan();
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return Infinity(result_sign);
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return Zero(result_sign);
+ }
+
+ DCHECK(lhs.IsFinite());
+ DCHECK(rhs.IsFinite());
+
+ if (rhs.IsZero())
+ return lhs.IsZero() ? Nan() : Infinity(result_sign);
+
+ int result_exponent = lhs.Exponent() - rhs.Exponent();
+
+ if (lhs.IsZero())
+ return Decimal(result_sign, result_exponent, 0);
+
+ uint64_t remainder = lhs.data_.Coefficient();
+ const uint64_t divisor = rhs.data_.Coefficient();
+ uint64_t result = 0;
+ for (;;) {
+ while (remainder < divisor && result < kMaxCoefficient / 10) {
+ remainder *= 10;
+ result *= 10;
+ --result_exponent;
+ }
+ if (remainder < divisor)
+ break;
+ uint64_t quotient = remainder / divisor;
+ if (result > kMaxCoefficient - quotient)
+ break;
+ result += quotient;
+ remainder %= divisor;
+ if (!remainder)
+ break;
+ }
+
+ if (remainder > divisor / 2)
+ ++result;
+
+ return Decimal(result_sign, result_exponent, result);
+}
+
+bool Decimal::operator==(const Decimal& rhs) const {
+ return data_ == rhs.data_ || CompareTo(rhs).IsZero();
+}
+
+bool Decimal::operator!=(const Decimal& rhs) const {
+ if (data_ == rhs.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 (data_ == rhs.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 (data_ == rhs.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.data_.SetSign(kPositive);
+ return result;
+}
+
+Decimal::AlignedOperands Decimal::AlignOperands(const Decimal& lhs,
+ const Decimal& rhs) {
+ DCHECK(lhs.IsFinite());
+ DCHECK(rhs.IsFinite());
+
+ const int lhs_exponent = lhs.Exponent();
+ const int rhs_exponent = rhs.Exponent();
+ int exponent = std::min(lhs_exponent, rhs_exponent);
+ uint64_t lhs_coefficient = lhs.data_.Coefficient();
+ uint64_t rhs_coefficient = rhs.data_.Coefficient();
+
+ if (lhs_exponent > rhs_exponent) {
+ const int number_of_lhs_digits = CountDigits(lhs_coefficient);
+ if (number_of_lhs_digits) {
+ const int lhs_shift_amount = lhs_exponent - rhs_exponent;
+ const int overflow = number_of_lhs_digits + lhs_shift_amount - kPrecision;
+ if (overflow <= 0) {
+ lhs_coefficient = ScaleUp(lhs_coefficient, lhs_shift_amount);
+ } else {
+ lhs_coefficient = ScaleUp(lhs_coefficient, lhs_shift_amount - overflow);
+ rhs_coefficient = ScaleDown(rhs_coefficient, overflow);
+ exponent += overflow;
+ }
+ }
+
+ } else if (lhs_exponent < rhs_exponent) {
+ const int number_of_rhs_digits = CountDigits(rhs_coefficient);
+ if (number_of_rhs_digits) {
+ const int rhs_shift_amount = rhs_exponent - lhs_exponent;
+ const int overflow = number_of_rhs_digits + rhs_shift_amount - kPrecision;
+ if (overflow <= 0) {
+ rhs_coefficient = ScaleUp(rhs_coefficient, rhs_shift_amount);
+ } else {
+ rhs_coefficient = ScaleUp(rhs_coefficient, rhs_shift_amount - overflow);
+ lhs_coefficient = ScaleDown(lhs_coefficient, overflow);
+ exponent += overflow;
+ }
+ }
+ }
+
+ AlignedOperands aligned_operands;
+ aligned_operands.exponent = exponent;
+ aligned_operands.lhs_coefficient = lhs_coefficient;
+ aligned_operands.rhs_coefficient = rhs_coefficient;
+ return aligned_operands;
+}
+
+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 = data_.Coefficient();
+ const int number_of_digits = CountDigits(result);
+ const int number_of_drop_digits = -Exponent();
+ if (number_of_digits <= number_of_drop_digits)
+ return IsPositive() ? Decimal(1) : Zero(kPositive);
+
+ result = ScaleDown(result, number_of_drop_digits);
+ if (IsPositive() &&
+ !IsMultiplePowersOfTen(data_.Coefficient(), number_of_drop_digits))
+ ++result;
+ return Decimal(GetSign(), 0, result);
+}
+
+Decimal Decimal::CompareTo(const Decimal& rhs) const {
+ const Decimal result(*this - rhs);
+ switch (result.data_.GetFormatClass()) {
+ case EncodedData::kClassInfinity:
+ return result.IsNegative() ? Decimal(-1) : Decimal(1);
+
+ case EncodedData::kClassNaN:
+ case EncodedData::kClassNormal:
+ return result;
+
+ case EncodedData::kClassZero:
+ return Zero(kPositive);
+
+ default:
+ NOTREACHED();
+ return Nan();
+ }
+}
+
+// Round toward negative infinity.
+Decimal Decimal::Floor() const {
+ if (IsSpecial())
+ return *this;
+
+ if (Exponent() >= 0)
+ return *this;
+
+ uint64_t result = data_.Coefficient();
+ const int number_of_digits = CountDigits(result);
+ const int number_of_drop_digits = -Exponent();
+ if (number_of_digits < number_of_drop_digits)
+ return IsPositive() ? Zero(kPositive) : Decimal(-1);
+
+ result = ScaleDown(result, number_of_drop_digits);
+ if (IsNegative() &&
+ !IsMultiplePowersOfTen(data_.Coefficient(), number_of_drop_digits))
+ ++result;
+ return Decimal(GetSign(), 0, result);
+}
+
+Decimal Decimal::FromDouble(double double_value) {
+ if (std::isfinite(double_value))
+ return FromString(String::NumberToStringECMAScript(double_value));
+
+ if (std::isinf(double_value))
+ return Infinity(double_value < 0 ? kNegative : kPositive);
+
+ return Nan();
+}
+
+Decimal Decimal::FromString(const String& str) {
+ int exponent = 0;
+ Sign exponent_sign = kPositive;
+ int number_of_digits = 0;
+ int number_of_digits_after_dot = 0;
+ int number_of_extra_digits = 0;
+ Sign sign = kPositive;
+
+ enum {
+ kStateDigit,
+ kStateDot,
+ kStateDotDigit,
+ kStateE,
+ kStateEDigit,
+ kStateESign,
+ kStateSign,
+ kStateStart,
+ kStateZero,
+ } state = kStateStart;
+
+#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 kStateDigit:
+ if (ch >= '0' && ch <= '9') {
+ if (number_of_digits < kPrecision) {
+ ++number_of_digits;
+ accumulator *= 10;
+ accumulator += ch - '0';
+ } else {
+ ++number_of_extra_digits;
+ }
+ break;
+ }
+
+ HandleCharAndBreak('.', kStateDot);
+ HandleTwoCharsAndBreak('E', 'e', kStateE);
+ return Nan();
+
+ case kStateDot:
+ case kStateDotDigit:
+ if (ch >= '0' && ch <= '9') {
+ if (number_of_digits < kPrecision) {
+ ++number_of_digits;
+ ++number_of_digits_after_dot;
+ accumulator *= 10;
+ accumulator += ch - '0';
+ }
+ state = kStateDotDigit;
+ break;
+ }
+
+ HandleTwoCharsAndBreak('E', 'e', kStateE);
+ return Nan();
+
+ case kStateE:
+ if (ch == '+') {
+ exponent_sign = kPositive;
+ state = kStateESign;
+ break;
+ }
+
+ if (ch == '-') {
+ exponent_sign = kNegative;
+ state = kStateESign;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ exponent = ch - '0';
+ state = kStateEDigit;
+ break;
+ }
+
+ return Nan();
+
+ case kStateEDigit:
+ if (ch >= '0' && ch <= '9') {
+ exponent *= 10;
+ exponent += ch - '0';
+ if (exponent > kExponentMax + kPrecision) {
+ if (accumulator)
+ return exponent_sign == kNegative ? Zero(kPositive)
+ : Infinity(sign);
+ return Zero(sign);
+ }
+ state = kStateEDigit;
+ break;
+ }
+
+ return Nan();
+
+ case kStateESign:
+ if (ch >= '0' && ch <= '9') {
+ exponent = ch - '0';
+ state = kStateEDigit;
+ break;
+ }
+
+ return Nan();
+
+ case kStateSign:
+ if (ch >= '1' && ch <= '9') {
+ accumulator = ch - '0';
+ number_of_digits = 1;
+ state = kStateDigit;
+ break;
+ }
+
+ HandleCharAndBreak('0', kStateZero);
+ return Nan();
+
+ case kStateStart:
+ if (ch >= '1' && ch <= '9') {
+ accumulator = ch - '0';
+ number_of_digits = 1;
+ state = kStateDigit;
+ break;
+ }
+
+ if (ch == '-') {
+ sign = kNegative;
+ state = kStateSign;
+ break;
+ }
+
+ if (ch == '+') {
+ sign = kPositive;
+ state = kStateSign;
+ break;
+ }
+
+ HandleCharAndBreak('0', kStateZero);
+ HandleCharAndBreak('.', kStateDot);
+ return Nan();
+
+ case kStateZero:
+ if (ch == '0')
+ break;
+
+ if (ch >= '1' && ch <= '9') {
+ accumulator = ch - '0';
+ number_of_digits = 1;
+ state = kStateDigit;
+ break;
+ }
+
+ HandleCharAndBreak('.', kStateDot);
+ HandleTwoCharsAndBreak('E', 'e', kStateE);
+ return Nan();
+
+ default:
+ NOTREACHED();
+ return Nan();
+ }
+ }
+
+ if (state == kStateZero)
+ return Zero(sign);
+
+ if (state == kStateDigit || state == kStateEDigit ||
+ state == kStateDotDigit) {
+ int result_exponent = exponent * (exponent_sign == kNegative ? -1 : 1) -
+ number_of_digits_after_dot + number_of_extra_digits;
+ if (result_exponent < kExponentMin)
+ return Zero(kPositive);
+
+ const int overflow = result_exponent - kExponentMax + 1;
+ if (overflow > 0) {
+ if (overflow + number_of_digits - number_of_digits_after_dot > kPrecision)
+ return Infinity(sign);
+ accumulator = ScaleUp(accumulator, overflow);
+ result_exponent -= overflow;
+ }
+
+ return Decimal(sign, result_exponent, accumulator);
+ }
+
+ return Nan();
+}
+
+Decimal Decimal::Infinity(const Sign sign) {
+ return Decimal(EncodedData(sign, EncodedData::kClassInfinity));
+}
+
+Decimal Decimal::Nan() {
+ return Decimal(EncodedData(kPositive, EncodedData::kClassNaN));
+}
+
+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 = data_.Coefficient();
+ const int number_of_digits = CountDigits(result);
+ const int number_of_drop_digits = -Exponent();
+ if (number_of_digits < number_of_drop_digits)
+ return Zero(kPositive);
+
+ result = ScaleDown(result, number_of_drop_digits - 1);
+ if (result % 10 >= 5)
+ result += 10;
+ result /= 10;
+ return Decimal(GetSign(), 0, result);
+}
+
+double Decimal::ToDouble() const {
+ if (IsFinite()) {
+ bool valid;
+ const double double_value = ToString().ToDouble(&valid);
+ return valid ? double_value : 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 (data_.GetFormatClass()) {
+ case EncodedData::kClassInfinity:
+ return GetSign() ? "-Infinity" : "Infinity";
+
+ case EncodedData::kClassNaN:
+ return "NaN";
+
+ case EncodedData::kClassNormal:
+ case EncodedData::kClassZero:
+ break;
+
+ default:
+ NOTREACHED();
+ return "";
+ }
+
+ StringBuilder builder;
+ if (GetSign())
+ builder.Append('-');
+
+ int original_exponent = Exponent();
+ uint64_t coefficient = data_.Coefficient();
+
+ if (original_exponent < 0) {
+ const int kMaxDigits = DBL_DIG;
+ uint64_t last_digit = 0;
+ while (CountDigits(coefficient) > kMaxDigits) {
+ last_digit = coefficient % 10;
+ coefficient /= 10;
+ ++original_exponent;
+ }
+
+ if (last_digit >= 5)
+ ++coefficient;
+
+ while (original_exponent < 0 && coefficient && !(coefficient % 10)) {
+ coefficient /= 10;
+ ++original_exponent;
+ }
+ }
+
+ const String digits = String::Number(coefficient);
+ int coefficient_length = static_cast<int>(digits.length());
+ const int adjusted_exponent = original_exponent + coefficient_length - 1;
+ if (original_exponent <= 0 && adjusted_exponent >= -6) {
+ if (!original_exponent) {
+ builder.Append(digits);
+ return builder.ToString();
+ }
+
+ if (adjusted_exponent >= 0) {
+ for (int i = 0; i < coefficient_length; ++i) {
+ builder.Append(digits[i]);
+ if (i == adjusted_exponent)
+ builder.Append('.');
+ }
+ return builder.ToString();
+ }
+
+ builder.Append("0.");
+ for (int i = adjusted_exponent + 1; i < 0; ++i)
+ builder.Append('0');
+
+ builder.Append(digits);
+
+ } else {
+ builder.Append(digits[0]);
+ while (coefficient_length >= 2 && digits[coefficient_length - 1] == '0')
+ --coefficient_length;
+ if (coefficient_length >= 2) {
+ builder.Append('.');
+ for (int i = 1; i < coefficient_length; ++i)
+ builder.Append(digits[i]);
+ }
+
+ if (adjusted_exponent) {
+ builder.Append(adjusted_exponent < 0 ? "e" : "e+");
+ builder.AppendNumber(adjusted_exponent);
+ }
+ }
+ return builder.ToString();
+}
+
+Decimal Decimal::Zero(Sign sign) {
+ return Decimal(EncodedData(sign, EncodedData::kClassZero));
+}
+
+std::ostream& operator<<(std::ostream& ostream, const Decimal& decimal) {
+ Decimal::EncodedData data = decimal.Value();
+ return ostream << "encode("
+ << String::Number(data.Coefficient()).Ascii().data() << ", "
+ << String::Number(data.Exponent()).Ascii().data() << ", "
+ << (data.GetSign() == Decimal::kNegative ? "Negative"
+ : "Positive")
+ << ")=" << decimal.ToString().Ascii().data();
+}
+
+} // namespace blink