mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This caused us to lose our gn check certification. :( Turns out gn check was just ignoring all the header paths it didn't understand and so gn check passing for sky wasn't meaning much. I tried to straighten out some of the mess in this CL, but its going to take several more rounds of massaging before gn check passes again. On the bright side (almost) all of our headers are absolute now. Turns out my script (attached to the bug) didn't notice ../ includes but I'll fix that in the next patch. R=abarth@chromium.org BUG=435361 Review URL: https://codereview.chromium.org/746023002
1027 lines
28 KiB
C++
1027 lines
28 KiB
C++
/*
|
|
* 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 "sky/engine/config.h"
|
|
#include "sky/engine/platform/Decimal.h"
|
|
|
|
#include "sky/engine/wtf/MathExtras.h"
|
|
#include "sky/engine/wtf/Noncopyable.h"
|
|
#include "sky/engine/wtf/text/StringBuilder.h"
|
|
|
|
#include <float.h>
|
|
#include <algorithm>
|
|
|
|
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 {
|
|
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, exponent, 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
|
|
{
|
|
return m_data == rhs.m_data || compareTo(rhs).isZero();
|
|
}
|
|
|
|
bool Decimal::operator!=(const Decimal& rhs) const
|
|
{
|
|
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 (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 (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.
|
|
// 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 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(String::numberToStringECMAScript(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.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);
|
|
|
|
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 = toString().toDouble(&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 = String::number(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();
|
|
}
|
|
|
|
Decimal Decimal::zero(Sign sign)
|
|
{
|
|
return Decimal(EncodedData(sign, EncodedData::ClassZero));
|
|
}
|
|
|
|
} // namespace blink
|