mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-02 08:55:53 +00:00
Refactor Number internals away from int128 to uint64 & a sign flag
This commit is contained in:
@@ -42,21 +42,20 @@ isPowerOfTen(T value)
|
||||
return logTen(value).has_value();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using numberuint = boost::multiprecision::uint128_t;
|
||||
using numberint = boost::multiprecision::int128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using numberuint = __uint128_t;
|
||||
using numberint = __int128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
// #ifdef _MSC_VER
|
||||
// using numberuint = boost::multiprecision::uint128_t;
|
||||
// using numberint = boost::multiprecision::int128_t;
|
||||
// #else // !defined(_MSC_VER)
|
||||
// using numberuint = __uint128_t;
|
||||
// using numberint = __int128_t;
|
||||
// #endif // !defined(_MSC_VER)
|
||||
|
||||
struct MantissaRange
|
||||
{
|
||||
using rep = std::int64_t;
|
||||
using internalrep = numberint;
|
||||
using rep = std::uint64_t;
|
||||
enum mantissa_scale { small, large };
|
||||
|
||||
explicit constexpr MantissaRange(mantissa_scale scale_, internalrep min_)
|
||||
explicit constexpr MantissaRange(mantissa_scale scale_, rep min_)
|
||||
: min(min_)
|
||||
, max(min_ * 10 - 1)
|
||||
, log(logTen(min).value_or(-1))
|
||||
@@ -65,19 +64,20 @@ struct MantissaRange
|
||||
}
|
||||
|
||||
rep min;
|
||||
internalrep max;
|
||||
rep max;
|
||||
int log;
|
||||
mantissa_scale scale;
|
||||
};
|
||||
|
||||
class Number
|
||||
{
|
||||
using uint128_t = numberuint;
|
||||
using int128_t = numberint;
|
||||
// using uint128_t = numberuint;
|
||||
// using int128_t = numberint;
|
||||
|
||||
using rep = MantissaRange::rep;
|
||||
using internalrep = MantissaRange::internalrep;
|
||||
using rep = std::int64_t;
|
||||
using internalrep = MantissaRange::rep;
|
||||
|
||||
bool negative_{false};
|
||||
internalrep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
@@ -106,11 +106,16 @@ public:
|
||||
Number(rep mantissa);
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
explicit constexpr Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept;
|
||||
// Only unit tests are expected to use this ctor
|
||||
explicit Number(internalrep mantissa, int exponent, normalized);
|
||||
explicit Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
normalized);
|
||||
|
||||
constexpr rep
|
||||
mantissa() const noexcept;
|
||||
@@ -158,7 +163,8 @@ public:
|
||||
friend constexpr bool
|
||||
operator==(Number const& x, Number const& y) noexcept
|
||||
{
|
||||
return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
||||
return x.negative_ == y.negative_ && x.mantissa_ == y.mantissa_ &&
|
||||
x.exponent_ == y.exponent_;
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
@@ -166,14 +172,13 @@ public:
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator<(Number const& x, Number const& y) noexcept
|
||||
{
|
||||
// If the two amounts have different signs (zero is treated as positive)
|
||||
// then the comparison is true iff the left is negative.
|
||||
bool const lneg = x.mantissa_ < 0;
|
||||
bool const rneg = y.mantissa_ < 0;
|
||||
bool const lneg = x.negative_;
|
||||
bool const rneg = y.negative_;
|
||||
|
||||
if (lneg != rneg)
|
||||
return lneg;
|
||||
@@ -201,7 +206,7 @@ public:
|
||||
constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
|
||||
return negative_ ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
Number
|
||||
@@ -268,8 +273,7 @@ public:
|
||||
static void
|
||||
setMantissaScale(MantissaRange::mantissa_scale scale);
|
||||
|
||||
template <class T = rep>
|
||||
inline static T
|
||||
inline static internalrep
|
||||
minMantissa()
|
||||
{
|
||||
return range_.get().min;
|
||||
@@ -308,8 +312,7 @@ private:
|
||||
static thread_local rounding_mode mode_;
|
||||
// The available ranges for mantissa
|
||||
|
||||
constexpr static internalrep maxRep =
|
||||
std::numeric_limits<std::int64_t>::max();
|
||||
constexpr static internalrep maxRep = std::numeric_limits<rep>::max();
|
||||
|
||||
constexpr static MantissaRange smallRange{
|
||||
MantissaRange::small,
|
||||
@@ -321,7 +324,7 @@ private:
|
||||
MantissaRange::large,
|
||||
1'000'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
// maxRep 9,223,372,036,854,775,808
|
||||
// maxRep 9,223,372,036,854,775,807
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(largeRange.log == 18);
|
||||
static_assert(largeRange.min < maxRep);
|
||||
@@ -335,9 +338,11 @@ private:
|
||||
void
|
||||
normalize();
|
||||
|
||||
template <class T>
|
||||
static void
|
||||
normalize(
|
||||
internalrep& mantissa,
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa);
|
||||
@@ -355,23 +360,31 @@ private:
|
||||
};
|
||||
|
||||
inline constexpr Number::Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent, normalized)
|
||||
: Number(mantissa, exponent, unchecked{})
|
||||
inline Number::Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
normalized)
|
||||
: Number(negative, mantissa, exponent, unchecked{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa, int exponent)
|
||||
: Number(mantissa, exponent, normalized{})
|
||||
: Number(
|
||||
mantissa < 0,
|
||||
mantissa < 0 ? -mantissa : mantissa,
|
||||
exponent,
|
||||
normalized{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
@@ -382,7 +395,7 @@ inline constexpr Number::rep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
auto m = mantissa_;
|
||||
while (m > maxRep)
|
||||
if (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || m % 10 == 0,
|
||||
@@ -390,7 +403,8 @@ Number::mantissa() const noexcept
|
||||
"large normalized mantissa has no remainder");
|
||||
m /= 10;
|
||||
}
|
||||
return static_cast<Number::rep>(m);
|
||||
auto const sign = negative_ ? -1 : 1;
|
||||
return sign * static_cast<Number::rep>(m);
|
||||
}
|
||||
|
||||
inline constexpr int
|
||||
@@ -398,7 +412,7 @@ Number::exponent() const noexcept
|
||||
{
|
||||
auto m = mantissa_;
|
||||
auto e = exponent_;
|
||||
while (m > maxRep)
|
||||
if (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || m % 10 == 0,
|
||||
@@ -419,8 +433,10 @@ Number::operator+() const noexcept
|
||||
inline constexpr Number
|
||||
Number::operator-() const noexcept
|
||||
{
|
||||
if (mantissa_ == 0)
|
||||
return Number{};
|
||||
auto x = *this;
|
||||
x.mantissa_ = -x.mantissa_;
|
||||
x.negative_ = !x.negative_;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -495,40 +511,45 @@ operator/(Number const& x, Number const& y)
|
||||
inline Number
|
||||
Number::min() noexcept
|
||||
{
|
||||
return Number{range_.get().min, minExponent, unchecked{}};
|
||||
return Number{false, range_.get().min, minExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline Number
|
||||
Number::max() noexcept
|
||||
{
|
||||
return Number{range_.get().max, maxExponent, unchecked{}};
|
||||
return Number{
|
||||
false, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline Number
|
||||
Number::lowest() noexcept
|
||||
{
|
||||
return -Number{range_.get().max, maxExponent, unchecked{}};
|
||||
return Number{
|
||||
true, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::isnormal() const noexcept
|
||||
{
|
||||
MantissaRange const& range = range_;
|
||||
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
|
||||
return range.min <= abs_m && abs_m <= range.max &&
|
||||
(abs_m <= maxRep || abs_m % 10 == 0) &&
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent;
|
||||
auto const abs_m = mantissa_;
|
||||
return *this == Number{} ||
|
||||
(range.min <= abs_m && abs_m <= range.max &&
|
||||
(abs_m <= maxRep || abs_m % 10 == 0) && minExponent <= exponent_ &&
|
||||
exponent_ <= maxExponent);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
||||
{
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
int exponent = exponent_;
|
||||
Number::normalize(mantissa, exponent, minMantissa, maxMantissa);
|
||||
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||
|
||||
return std::make_pair(static_cast<T>(mantissa), exponent);
|
||||
auto const sign = negative ? -1 : 1;
|
||||
return std::make_pair(static_cast<T>(sign * mantissa), exponent);
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
|
||||
@@ -21,17 +21,17 @@ using uint128_t = boost::multiprecision::uint128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using uint128_t = __uint128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
static_assert(std::is_same_v<uint128_t, ripple::numberuint>);
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct make_unsigned<ripple::numberint>
|
||||
{
|
||||
using type = uint128_t;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
// static_assert(std::is_same_v<uint128_t, ripple::numberuint>);
|
||||
//
|
||||
// namespace std {
|
||||
//
|
||||
// template <>
|
||||
// struct make_unsigned<ripple::numberint>
|
||||
//{
|
||||
// using type = uint128_t;
|
||||
// };
|
||||
//
|
||||
// } // namespace std
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -60,7 +60,6 @@ Number::getMantissaScale()
|
||||
void
|
||||
Number::setMantissaScale(MantissaRange::mantissa_scale scale)
|
||||
{
|
||||
// scale_ and range_ MUST stay in lockstep
|
||||
if (scale != MantissaRange::small && scale != MantissaRange::large)
|
||||
LogicError("Unknown mantissa scale");
|
||||
range_ = scale == MantissaRange::small ? smallRange : largeRange;
|
||||
@@ -132,7 +131,7 @@ Number::Guard::is_negative() const noexcept
|
||||
inline void
|
||||
Number::Guard::doPush(unsigned d) noexcept
|
||||
{
|
||||
xbit_ = xbit_ || (digits_ & 0x0000'0000'0000'000F) != 0;
|
||||
xbit_ = xbit_ || ((digits_ & 0x0000'0000'0000'000F) != 0);
|
||||
digits_ >>= 4;
|
||||
digits_ |= (d & 0x0000'0000'0000'000FULL) << 60;
|
||||
}
|
||||
@@ -199,7 +198,10 @@ constexpr Number
|
||||
Number::oneSmall()
|
||||
{
|
||||
return Number{
|
||||
Number::smallRange.min, -Number::smallRange.log, Number::unchecked{}};
|
||||
false,
|
||||
Number::smallRange.min,
|
||||
-Number::smallRange.log,
|
||||
Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number oneSml = Number::oneSmall();
|
||||
@@ -208,7 +210,10 @@ constexpr Number
|
||||
Number::oneLarge()
|
||||
{
|
||||
return Number{
|
||||
Number::largeRange.min, -Number::largeRange.log, Number::unchecked{}};
|
||||
false,
|
||||
Number::largeRange.min,
|
||||
-Number::largeRange.log,
|
||||
Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number oneLrg = Number::oneLarge();
|
||||
@@ -223,9 +228,11 @@ Number::one()
|
||||
}
|
||||
|
||||
// Use the member names in this static function for now so the diff is cleaner
|
||||
template <class T>
|
||||
void
|
||||
Number::normalize(
|
||||
internalrep& mantissa_,
|
||||
bool& negative,
|
||||
T& mantissa_,
|
||||
int& exponent_,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa)
|
||||
@@ -235,11 +242,10 @@ Number::normalize(
|
||||
{
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
negative = zero.negative_;
|
||||
return;
|
||||
}
|
||||
bool const negative = (mantissa_ < 0);
|
||||
auto m = static_cast<std::make_unsigned_t<internalrep>>(
|
||||
negative ? -mantissa_ : mantissa_);
|
||||
auto m = mantissa_;
|
||||
while ((m < minMantissa) && (exponent_ > minExponent))
|
||||
{
|
||||
m *= 10;
|
||||
@@ -260,6 +266,7 @@ Number::normalize(
|
||||
{
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
negative = zero.negative_;
|
||||
return;
|
||||
}
|
||||
// When using the largeRange, "m" needs fit within an int64, even if
|
||||
@@ -312,16 +319,17 @@ Number::normalize(
|
||||
mantissa_ >= minMantissa && mantissa_ <= maxMantissa,
|
||||
"ripple::Number::normalize",
|
||||
"final mantissa fits in range");
|
||||
|
||||
if (negative)
|
||||
mantissa_ = -mantissa_;
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize()
|
||||
{
|
||||
normalize(
|
||||
mantissa_, exponent_, Number::minMantissa(), Number::maxMantissa());
|
||||
negative_,
|
||||
mantissa_,
|
||||
exponent_,
|
||||
Number::minMantissa(),
|
||||
Number::maxMantissa());
|
||||
}
|
||||
|
||||
// Copy the number, but set a new exponent. Because the mantissa doesn't change,
|
||||
@@ -339,7 +347,7 @@ Number::shiftExponent(int exponentDelta) const
|
||||
{
|
||||
return Number{};
|
||||
}
|
||||
Number const result{mantissa_, newExponent, unchecked{}};
|
||||
Number const result{negative_, mantissa_, newExponent, unchecked{}};
|
||||
XRPL_ASSERT_PARTS(
|
||||
result.isnormal(),
|
||||
"ripple::Number::shiftExponent",
|
||||
@@ -350,41 +358,41 @@ Number::shiftExponent(int exponentDelta) const
|
||||
Number&
|
||||
Number::operator+=(Number const& y)
|
||||
{
|
||||
if (y == Number{})
|
||||
constexpr Number zero = Number{};
|
||||
if (y == zero)
|
||||
return *this;
|
||||
if (*this == Number{})
|
||||
if (*this == zero)
|
||||
{
|
||||
*this = y;
|
||||
return *this;
|
||||
}
|
||||
if (*this == -y)
|
||||
{
|
||||
*this = Number{};
|
||||
*this = zero;
|
||||
return *this;
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
isnormal() && y.isnormal(),
|
||||
"ripple::Number::operator+=(Number) : is normal");
|
||||
auto xm = mantissa_;
|
||||
auto xe = exponent_;
|
||||
int xn = 1;
|
||||
if (xm < 0)
|
||||
{
|
||||
xm = -xm;
|
||||
xn = -1;
|
||||
}
|
||||
auto ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
int yn = 1;
|
||||
if (ym < 0)
|
||||
{
|
||||
ym = -ym;
|
||||
yn = -1;
|
||||
}
|
||||
// *n = negative
|
||||
// *s = sign
|
||||
// *m = mantissa
|
||||
// *e = exponent
|
||||
|
||||
bool xn = negative_;
|
||||
int xs = xn ? -1 : 1;
|
||||
internalrep xm = xs * mantissa();
|
||||
auto xe = exponent();
|
||||
|
||||
bool yn = y.negative_;
|
||||
int ys = yn ? -1 : 1;
|
||||
internalrep ym = ys * y.mantissa();
|
||||
auto ye = y.exponent();
|
||||
Guard g;
|
||||
if (xe < ye)
|
||||
{
|
||||
if (xn == -1)
|
||||
if (xn)
|
||||
g.set_negative();
|
||||
do
|
||||
{
|
||||
@@ -395,7 +403,7 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
else if (xe > ye)
|
||||
{
|
||||
if (yn == -1)
|
||||
if (yn)
|
||||
g.set_negative();
|
||||
do
|
||||
{
|
||||
@@ -404,12 +412,15 @@ Number::operator+=(Number const& y)
|
||||
++ye;
|
||||
} while (xe > ye);
|
||||
}
|
||||
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
|
||||
if (xn == yn)
|
||||
{
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
|
||||
xm += ym;
|
||||
if (xm > maxMantissa)
|
||||
if (xm > maxMantissa || xm > maxRep)
|
||||
{
|
||||
g.push(xm % 10);
|
||||
xm /= 10;
|
||||
@@ -419,7 +430,7 @@ Number::operator+=(Number const& y)
|
||||
if (r == 1 || (r == 0 && (xm & 1) == 1))
|
||||
{
|
||||
++xm;
|
||||
if (xm > maxMantissa)
|
||||
if (xm > maxMantissa || xm > maxRep)
|
||||
{
|
||||
xm /= 10;
|
||||
++xe;
|
||||
@@ -430,8 +441,6 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
|
||||
if (xm > ym)
|
||||
{
|
||||
xm = xm - ym;
|
||||
@@ -442,7 +451,7 @@ Number::operator+=(Number const& y)
|
||||
xe = ye;
|
||||
xn = yn;
|
||||
}
|
||||
while (xm < minMantissa)
|
||||
while (xm < minMantissa && xm * 10 <= maxRep)
|
||||
{
|
||||
xm *= 10;
|
||||
xm -= g.pop();
|
||||
@@ -452,24 +461,32 @@ Number::operator+=(Number const& y)
|
||||
if (r == 1 || (r == 0 && (xm & 1) == 1))
|
||||
{
|
||||
--xm;
|
||||
if (xm < minMantissa)
|
||||
if (xm < minMantissa && xm * 10 <= maxRep)
|
||||
{
|
||||
xm *= 10;
|
||||
--xe;
|
||||
}
|
||||
}
|
||||
if (xe < minExponent)
|
||||
{
|
||||
xm = 0;
|
||||
xe = Number{}.exponent_;
|
||||
}
|
||||
}
|
||||
mantissa_ = xm * xn;
|
||||
// Bring xm into the minMantissa / maxMantissa range AFTER rounding
|
||||
if (xm < minMantissa)
|
||||
{
|
||||
xm *= 10;
|
||||
--xe;
|
||||
}
|
||||
if (xe < minExponent)
|
||||
{
|
||||
xm = zero.mantissa_;
|
||||
xe = zero.exponent_;
|
||||
xn = zero.negative_;
|
||||
}
|
||||
|
||||
negative_ = xn;
|
||||
mantissa_ = xm;
|
||||
exponent_ = xe;
|
||||
normalize();
|
||||
XRPL_ASSERT(
|
||||
isnormal() || *this == Number{},
|
||||
"ripple::Number::operator+=(Number) : result is normal");
|
||||
isnormal(), "ripple::Number::operator+=(Number) : result is normal");
|
||||
normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -504,9 +521,10 @@ divu10(uint128_t& u)
|
||||
Number&
|
||||
Number::operator*=(Number const& y)
|
||||
{
|
||||
if (*this == Number{})
|
||||
constexpr Number zero = Number{};
|
||||
if (*this == zero)
|
||||
return *this;
|
||||
if (y == Number{})
|
||||
if (y == zero)
|
||||
{
|
||||
*this = y;
|
||||
return *this;
|
||||
@@ -514,32 +532,33 @@ Number::operator*=(Number const& y)
|
||||
XRPL_ASSERT(
|
||||
isnormal() && y.isnormal(),
|
||||
"ripple::Number::operator*=(Number) : is normal");
|
||||
auto xm = mantissa_;
|
||||
auto xe = exponent_;
|
||||
int xn = 1;
|
||||
if (xm < 0)
|
||||
{
|
||||
xm = -xm;
|
||||
xn = -1;
|
||||
}
|
||||
auto ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
int yn = 1;
|
||||
if (ym < 0)
|
||||
{
|
||||
ym = -ym;
|
||||
yn = -1;
|
||||
}
|
||||
// *n = negative
|
||||
// *s = sign
|
||||
// *m = mantissa
|
||||
// *e = exponent
|
||||
|
||||
bool xn = negative_;
|
||||
int xs = xn ? -1 : 1;
|
||||
internalrep xm = xs * mantissa();
|
||||
auto xe = exponent();
|
||||
|
||||
bool yn = y.negative_;
|
||||
int ys = yn ? -1 : 1;
|
||||
internalrep ym = ys * y.mantissa();
|
||||
auto ye = y.exponent();
|
||||
|
||||
auto zm = uint128_t(xm) * uint128_t(ym);
|
||||
auto ze = xe + ye;
|
||||
auto zn = xn * yn;
|
||||
auto zs = xs * ys;
|
||||
bool zn = (zs == -1);
|
||||
Guard g;
|
||||
if (zn == -1)
|
||||
if (zn)
|
||||
g.set_negative();
|
||||
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
|
||||
while (zm > maxMantissa)
|
||||
while (zm > maxMantissa || zm > maxRep)
|
||||
{
|
||||
// The following is optimization for:
|
||||
// g.push(static_cast<unsigned>(zm % 10));
|
||||
@@ -553,53 +572,62 @@ Number::operator*=(Number const& y)
|
||||
if (r == 1 || (r == 0 && (xm & 1) == 1))
|
||||
{
|
||||
++xm;
|
||||
if (xm > maxMantissa)
|
||||
if (xm > maxMantissa || xm > maxRep)
|
||||
{
|
||||
xm /= 10;
|
||||
++xe;
|
||||
}
|
||||
}
|
||||
// Bring xm back into the minMantissa / maxMantissa range
|
||||
if (xm < minMantissa)
|
||||
{
|
||||
xm *= 10;
|
||||
--xe;
|
||||
}
|
||||
if (xe < minExponent)
|
||||
{
|
||||
xm = 0;
|
||||
xe = Number{}.exponent_;
|
||||
xm = zero.mantissa_;
|
||||
xe = zero.exponent_;
|
||||
zn = zero.negative_;
|
||||
}
|
||||
if (xe > maxExponent)
|
||||
throw std::overflow_error(
|
||||
"Number::multiplication overflow : exponent is " +
|
||||
std::to_string(xe));
|
||||
mantissa_ = xm * zn;
|
||||
negative_ = zn;
|
||||
mantissa_ = xm;
|
||||
exponent_ = xe;
|
||||
normalize();
|
||||
XRPL_ASSERT(
|
||||
isnormal() || *this == Number{},
|
||||
"ripple::Number::operator*=(Number) : result is normal");
|
||||
isnormal(), "ripple::Number::operator*=(Number) : result is normal");
|
||||
normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator/=(Number const& y)
|
||||
{
|
||||
if (y == Number{})
|
||||
constexpr Number zero = Number{};
|
||||
if (y == zero)
|
||||
throw std::overflow_error("Number: divide by 0");
|
||||
if (*this == Number{})
|
||||
if (*this == zero)
|
||||
return *this;
|
||||
int np = 1;
|
||||
// n* = numerator
|
||||
// d* = denominator
|
||||
// *p = negative (positive?)
|
||||
// *s = sign
|
||||
// *m = mantissa
|
||||
// *e = exponent
|
||||
|
||||
bool np = negative_;
|
||||
int ns = (np ? -1 : 1);
|
||||
auto nm = mantissa_;
|
||||
auto ne = exponent_;
|
||||
if (nm < 0)
|
||||
{
|
||||
nm = -nm;
|
||||
np = -1;
|
||||
}
|
||||
int dp = 1;
|
||||
|
||||
bool dp = y.negative_;
|
||||
int ds = (dp ? -1 : 1);
|
||||
auto dm = y.mantissa_;
|
||||
auto de = y.exponent_;
|
||||
if (dm < 0)
|
||||
{
|
||||
dm = -dm;
|
||||
dp = -1;
|
||||
}
|
||||
|
||||
// Shift by 10^17 gives greatest precision while not overflowing
|
||||
// uint128_t or the cast back to int64_t
|
||||
// TODO: Can/should this be made bigger for largeRange?
|
||||
@@ -612,7 +640,7 @@ Number::operator/=(Number const& y)
|
||||
uint128_t const f =
|
||||
small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL;
|
||||
XRPL_ASSERT_PARTS(
|
||||
f >= Number::minMantissa<internalrep>() * 10,
|
||||
f >= Number::minMantissa() * 10,
|
||||
"Number::operator/=",
|
||||
"factor expected size");
|
||||
|
||||
@@ -620,14 +648,15 @@ Number::operator/=(Number const& y)
|
||||
auto const dmu = static_cast<uint128_t>(dm);
|
||||
// correctionFactor can be anything between 10 and f, depending on how much
|
||||
// extra precision we want to only use for rounding with the
|
||||
// largeMantissa. Three digits seems like plenty, and is more than
|
||||
// the smallMantissa uses.
|
||||
// largeRange. Three digits seems like plenty, and is more than
|
||||
// the smallRange uses.
|
||||
uint128_t const correctionFactor = 1'000;
|
||||
|
||||
auto const numerator = uint128_t(nm) * f;
|
||||
|
||||
mantissa_ = numerator / dmu;
|
||||
exponent_ = ne - de - (small ? 17 : 19);
|
||||
auto zm = numerator / dmu;
|
||||
auto ze = ne - de - (small ? 17 : 19);
|
||||
bool zn = (ns * ds) < 0;
|
||||
if (!small)
|
||||
{
|
||||
// Virtually multiply numerator by correctionFactor. Since that would
|
||||
@@ -652,27 +681,31 @@ Number::operator/=(Number const& y)
|
||||
auto const remainder = (numerator % dmu);
|
||||
if (remainder != 0)
|
||||
{
|
||||
mantissa_ *= correctionFactor;
|
||||
zm *= correctionFactor;
|
||||
auto const correction = remainder * correctionFactor / dmu;
|
||||
mantissa_ += correction;
|
||||
zm += correction;
|
||||
// divide by 1000 by moving the exponent, so we don't lose the
|
||||
// integer value we just computed
|
||||
exponent_ -= 3;
|
||||
ze -= 3;
|
||||
}
|
||||
}
|
||||
mantissa_ *= np * dp;
|
||||
normalize();
|
||||
normalize(zn, zm, ze, minMantissa(), maxMantissa());
|
||||
negative_ = zn;
|
||||
mantissa_ = static_cast<internalrep>(zm);
|
||||
exponent_ = ze;
|
||||
XRPL_ASSERT_PARTS(
|
||||
isnormal(), "ripple::Number::operator/=", "result is normalized");
|
||||
return *this;
|
||||
}
|
||||
|
||||
Number::operator rep() const
|
||||
{
|
||||
internalrep drops = mantissa_;
|
||||
int offset = exponent_;
|
||||
rep drops = mantissa();
|
||||
int offset = exponent();
|
||||
Guard g;
|
||||
if (drops != 0)
|
||||
{
|
||||
if (drops < 0)
|
||||
if (negative_)
|
||||
{
|
||||
g.set_negative();
|
||||
drops = -drops;
|
||||
@@ -682,13 +715,6 @@ Number::operator rep() const
|
||||
g.push(drops % 10);
|
||||
drops /= 10;
|
||||
}
|
||||
if (offset == 0 && drops > std::numeric_limits<rep>::max())
|
||||
{
|
||||
// If offset == 0, then the loop won't run, and the overflow check
|
||||
// won't be made, but a int128 can overflow int64 by itself, so
|
||||
// check here.
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
}
|
||||
for (; offset > 0; --offset)
|
||||
{
|
||||
if (drops > std::numeric_limits<rep>::max() / 10)
|
||||
@@ -703,37 +729,27 @@ Number::operator rep() const
|
||||
if (g.is_negative())
|
||||
drops = -drops;
|
||||
}
|
||||
return static_cast<rep>(drops);
|
||||
return drops;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(Number const& amount)
|
||||
{
|
||||
// keep full internal accuracy, but make more human friendly if possible
|
||||
if (amount == Number{})
|
||||
constexpr Number zero = Number{};
|
||||
if (amount == zero)
|
||||
return "0";
|
||||
|
||||
auto const exponent = amount.exponent_;
|
||||
|
||||
bool const negative = amount.mantissa_ < 0;
|
||||
|
||||
auto const mantissa = [&]() {
|
||||
auto mantissa = amount.mantissa_;
|
||||
if (negative)
|
||||
{
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
XRPL_ASSERT(
|
||||
mantissa < std::numeric_limits<std::uint64_t>::max(),
|
||||
"ripple::to_string(Number) : mantissa fits in uin64_t");
|
||||
return static_cast<std::uint64_t>(mantissa);
|
||||
}();
|
||||
auto const mantissa = amount.mantissa_;
|
||||
bool const negative = amount.negative_;
|
||||
|
||||
// Use scientific notation for exponents that are too small or too large
|
||||
auto const rangeLog = Number::mantissaLog();
|
||||
if (((exponent != 0) &&
|
||||
((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
|
||||
{
|
||||
// TODO: trim trailing zeros off mantissa
|
||||
std::string ret = negative ? "-" : "";
|
||||
ret.append(std::to_string(mantissa));
|
||||
ret.append(1, 'e');
|
||||
@@ -741,14 +757,11 @@ to_string(Number const& amount)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: These numbers are probably wrong for largeRange
|
||||
XRPL_ASSERT(
|
||||
exponent + 43 > 0, "ripple::to_string(Number) : minimum exponent");
|
||||
|
||||
auto const mantissaLog = Number::mantissaLog();
|
||||
|
||||
ptrdiff_t const pad_prefix = mantissaLog + 12;
|
||||
ptrdiff_t const pad_suffix = mantissaLog + 8;
|
||||
ptrdiff_t const pad_prefix = rangeLog + 12;
|
||||
ptrdiff_t const pad_suffix = rangeLog + 8;
|
||||
|
||||
std::string const raw_value(std::to_string(mantissa));
|
||||
std::string val;
|
||||
@@ -758,7 +771,7 @@ to_string(Number const& amount)
|
||||
val.append(raw_value);
|
||||
val.append(pad_suffix, '0');
|
||||
|
||||
ptrdiff_t const offset(exponent + pad_prefix + mantissaLog + 1);
|
||||
ptrdiff_t const offset(exponent + pad_prefix + rangeLog + 1);
|
||||
|
||||
auto pre_from(val.begin());
|
||||
auto const pre_to(val.begin() + offset);
|
||||
@@ -841,6 +854,7 @@ power(Number const& f, unsigned n)
|
||||
Number
|
||||
root(Number f, unsigned d)
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
auto const one = Number::one();
|
||||
|
||||
if (f == one || d == 1)
|
||||
@@ -850,12 +864,12 @@ root(Number f, unsigned d)
|
||||
if (f == -one)
|
||||
return one;
|
||||
if (abs(f) < one)
|
||||
return Number{};
|
||||
return zero;
|
||||
throw std::overflow_error("Number::root infinity");
|
||||
}
|
||||
if (f < Number{} && d % 2 == 0)
|
||||
if (f < zero && d % 2 == 0)
|
||||
throw std::overflow_error("Number::root nan");
|
||||
if (f == Number{})
|
||||
if (f == zero)
|
||||
return f;
|
||||
|
||||
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
||||
@@ -874,7 +888,7 @@ root(Number f, unsigned d)
|
||||
XRPL_ASSERT_PARTS(
|
||||
f.isnormal(), "ripple::root(Number, unsigned)", "f is normalized");
|
||||
bool neg = false;
|
||||
if (f < Number{})
|
||||
if (f < zero)
|
||||
{
|
||||
neg = true;
|
||||
f = -f;
|
||||
@@ -915,13 +929,14 @@ root(Number f, unsigned d)
|
||||
Number
|
||||
root2(Number f)
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
auto const one = Number::one();
|
||||
|
||||
if (f == one)
|
||||
return f;
|
||||
if (f < Number{})
|
||||
if (f < zero)
|
||||
throw std::overflow_error("Number::root nan");
|
||||
if (f == Number{})
|
||||
if (f == zero)
|
||||
return f;
|
||||
|
||||
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
||||
@@ -962,6 +977,7 @@ root2(Number f)
|
||||
Number
|
||||
power(Number const& f, unsigned n, unsigned d)
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
auto const one = Number::one();
|
||||
|
||||
if (f == one)
|
||||
@@ -974,7 +990,7 @@ power(Number const& f, unsigned n, unsigned d)
|
||||
if (f == -one)
|
||||
return one;
|
||||
if (abs(f) < one)
|
||||
return Number{};
|
||||
return zero;
|
||||
// abs(f) > one
|
||||
throw std::overflow_error("Number::power infinity");
|
||||
}
|
||||
@@ -982,7 +998,7 @@ power(Number const& f, unsigned n, unsigned d)
|
||||
return one;
|
||||
n /= g;
|
||||
d /= g;
|
||||
if ((n % 2) == 1 && (d % 2) == 0 && f < Number{})
|
||||
if ((n % 2) == 1 && (d % 2) == 0 && f < zero)
|
||||
throw std::overflow_error("Number::power nan");
|
||||
return root(power(f, n), d);
|
||||
}
|
||||
|
||||
@@ -851,12 +851,11 @@ STAmount::canonicalize()
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
auto const value = unsafe_cast<std::int64_t>(mValue);
|
||||
Number num(
|
||||
mIsNegative ? -value : value, mOffset, Number::unchecked{});
|
||||
Number num(mIsNegative, mValue, mOffset, Number::unchecked{});
|
||||
auto set = [&](auto const& val) {
|
||||
mIsNegative = val.value() < 0;
|
||||
mValue = mIsNegative ? -val.value() : val.value();
|
||||
auto const value = val.value();
|
||||
mIsNegative = value < 0;
|
||||
mValue = mIsNegative ? -value : value;
|
||||
};
|
||||
if (native())
|
||||
set(XRPAmount{num});
|
||||
|
||||
@@ -191,7 +191,7 @@ numberFromJson(SField const& field, Json::Value const& value)
|
||||
// Number mantissas are much bigger than the allowable parsed values, so
|
||||
// it can't be out of range.
|
||||
static_assert(
|
||||
std::numeric_limits<numberint>::max() >=
|
||||
std::numeric_limits<std::uint64_t>::max() >=
|
||||
std::numeric_limits<decltype(parts.mantissa)>::max());
|
||||
}
|
||||
else
|
||||
@@ -199,11 +199,13 @@ numberFromJson(SField const& field, Json::Value const& value)
|
||||
Throw<std::runtime_error>("not a number");
|
||||
}
|
||||
|
||||
numberint mantissa = parts.mantissa;
|
||||
if (parts.negative)
|
||||
mantissa = -mantissa;
|
||||
|
||||
return STNumber{field, Number{mantissa, parts.exponent, Number::normalized{}}};
|
||||
return STNumber{
|
||||
field,
|
||||
Number{
|
||||
parts.negative,
|
||||
parts.mantissa,
|
||||
parts.exponent,
|
||||
Number::normalized{}}};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -34,10 +34,11 @@ public:
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "test_limits " << to_string(scale);
|
||||
bool caught = false;
|
||||
auto const minMantissa = Number::minMantissa<numberint>();
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
try
|
||||
{
|
||||
Number x = Number{minMantissa * 10, 32768, Number::normalized{}};
|
||||
Number x =
|
||||
Number{false, minMantissa * 10, 32768, Number::normalized{}};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -53,12 +54,14 @@ public:
|
||||
};
|
||||
|
||||
test(
|
||||
Number{minMantissa * 10, 32767, Number::normalized{}},
|
||||
Number{minMantissa, 32768, Number::normalized{}});
|
||||
test(Number{minMantissa, -32769, Number::normalized{}}, Number{});
|
||||
Number{false, minMantissa * 10, 32767, Number::normalized{}},
|
||||
Number{false, minMantissa, 32768, Number::normalized{}});
|
||||
test(
|
||||
Number{minMantissa * 1'000 + 1'500, 32000, Number::normalized{}},
|
||||
Number{minMantissa + 2, 32003, Number::normalized{}});
|
||||
Number{false, minMantissa, -32769, Number::normalized{}}, Number{});
|
||||
test(
|
||||
Number{false, minMantissa, 32000, Number::normalized{}} * 1'000 +
|
||||
Number{false, 1'500, 32000, Number::normalized{}},
|
||||
Number{false, minMantissa + 2, 32003, Number::normalized{}});
|
||||
// 9,223,372,036,854,775,808
|
||||
|
||||
test(
|
||||
@@ -79,7 +82,7 @@ public:
|
||||
try
|
||||
{
|
||||
Number q =
|
||||
Number{minMantissa * 100 - 1, 32767, Number::normalized{}};
|
||||
Number{false, minMantissa, 32767, Number::normalized{}} * 100;
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -129,13 +132,15 @@ public:
|
||||
{Number{-1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{
|
||||
-(numberint(9'999'999'999'999'344) * 1'000 + 444),
|
||||
true,
|
||||
9'999'999'999'999'344'444ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{-6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'344) * 1'000 + 444,
|
||||
false,
|
||||
9'999'999'999'999'344'444ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{}, Number{5}, Number{5}},
|
||||
@@ -156,13 +161,15 @@ public:
|
||||
{Number{-1'000'000'000'000'000'000, -18},
|
||||
Number{6'555'555'555'555'555'555, -35},
|
||||
Number{
|
||||
-(numberint(9'999'999'999'999'999) * 1'000 + 344),
|
||||
true,
|
||||
9'999'999'999'999'999'344ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{-6'555'555'555'555'555'555, -35},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1'000 + 344,
|
||||
false,
|
||||
9'999'999'999'999'999'344ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{}, Number{5}, Number{5}},
|
||||
@@ -170,12 +177,14 @@ public:
|
||||
Number{-5'555'555'555'555'555'554, -32768},
|
||||
Number{0}},
|
||||
{Number{
|
||||
-(numberint(9'999'999'999'999'999) * 1'000 + 999),
|
||||
true,
|
||||
9'999'999'999'999'999'999ULL,
|
||||
-37,
|
||||
Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1'000 + 990,
|
||||
false,
|
||||
9'999'999'999'999'999'990ULL,
|
||||
-19,
|
||||
Number::normalized{}}}});
|
||||
auto test = [this](auto const& c) {
|
||||
@@ -195,8 +204,14 @@ public:
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{Number::maxMantissa(), 32768, Number::normalized{}} +
|
||||
Number{Number::minMantissa() * 5, 32767};
|
||||
Number{
|
||||
false, Number::maxMantissa(), 32768, Number::normalized{}} +
|
||||
Number{
|
||||
false,
|
||||
Number::minMantissa(),
|
||||
32767,
|
||||
Number::normalized{}} *
|
||||
5;
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -236,13 +251,15 @@ public:
|
||||
{{Number{1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'344) * 1'000 + 444,
|
||||
false,
|
||||
9'999'999'999'999'344'444ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{
|
||||
-(numberint(9'999'999'999'999'344) * 1'000 + 444),
|
||||
true,
|
||||
9'999'999'999'999'344'444ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{1'000'000'000'000'000, -15},
|
||||
@@ -258,13 +275,15 @@ public:
|
||||
{Number{1'000'000'000'000'000'000, -18},
|
||||
Number{6'555'555'555'555'555'555, -32},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'344) * 1'000 + 444,
|
||||
false,
|
||||
9'999'999'999'999'344'444ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{6'555'555'555'555'555'555, -32},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{
|
||||
-(numberint(9'999'999'999'999'344) * 1'000 + 444),
|
||||
true,
|
||||
9'999'999'999'999'344'444ULL,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{1'000'000'000'000'000'000, -18},
|
||||
@@ -357,7 +376,8 @@ public:
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1000 + 579,
|
||||
false,
|
||||
9'999'999'999'999'999'579ULL,
|
||||
-18,
|
||||
Number::normalized{}}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
@@ -379,8 +399,8 @@ public:
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0}},
|
||||
// Maximum mantissa range - rounds up to 1e19
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa, 0, Number::normalized{}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38}},
|
||||
// Maximum int64 range
|
||||
{Number{maxInt64, 0},
|
||||
@@ -428,7 +448,8 @@ public:
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{
|
||||
numberint(9999999999999999) * 1000 + 579,
|
||||
false,
|
||||
9999999999999999579ULL,
|
||||
-18,
|
||||
Number::normalized{}}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
@@ -451,9 +472,13 @@ public:
|
||||
Number{10, 0}},
|
||||
// Maximum mantissa range - rounds down to maxMantissa/10e1
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa / 10 - 1, 20, Number::normalized{}}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{
|
||||
false,
|
||||
maxMantissa / 10 - 1,
|
||||
20,
|
||||
Number::normalized{}}},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{maxInt64, 0},
|
||||
@@ -501,7 +526,8 @@ public:
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1000 + 579,
|
||||
false,
|
||||
9'999'999'999'999'999'579ULL,
|
||||
-18,
|
||||
Number::normalized{}}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
@@ -524,9 +550,13 @@ public:
|
||||
Number{10, 0}},
|
||||
// Maximum mantissa range - rounds down to maxMantissa/10e1
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa / 10 - 1, 20, Number::normalized{}}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{
|
||||
false,
|
||||
maxMantissa / 10 - 1,
|
||||
20,
|
||||
Number::normalized{}}},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{maxInt64, 0},
|
||||
@@ -594,8 +624,8 @@ public:
|
||||
Number{1000000000000000001, -17}},
|
||||
// Maximum mantissa range - rounds up to minMantissa*10
|
||||
// 1e19*1e19=1e38
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa, 0, Number::normalized{}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38}},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
@@ -611,8 +641,12 @@ public:
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{maxMantissa, 32768, Number::normalized{}} *
|
||||
Number{Number::minMantissa() * 5, 32767};
|
||||
Number{false, maxMantissa, 32768, Number::normalized{}} *
|
||||
Number{
|
||||
false,
|
||||
Number::minMantissa() * 5,
|
||||
32767,
|
||||
Number::normalized{}};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -685,9 +719,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
Number{false, maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -732,9 +766,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
Number{false, maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -779,9 +813,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
Number{false, maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -826,9 +860,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
Number{false, maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -880,7 +914,21 @@ public:
|
||||
{Number{5, -1}, 0, Number{0}},
|
||||
{Number{0}, 5, Number{0}},
|
||||
{Number{5625, -4}, 2, Number{75, -2}}});
|
||||
auto const cLarge = std::to_array<Case>({
|
||||
{Number{false, Number::maxMantissa() - 9, -1, Number::normalized{}},
|
||||
2,
|
||||
Number{false, 999'999'999'999'999'999, -9, Number::normalized{}}},
|
||||
{Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}},
|
||||
2,
|
||||
Number{
|
||||
false, 3'162'277'660'168'379'330, -9, Number::normalized{}}},
|
||||
});
|
||||
test(cSmall);
|
||||
if (Number::getMantissaScale() != MantissaRange::small)
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
test(cLarge);
|
||||
}
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
@@ -957,16 +1005,10 @@ public:
|
||||
{Number{-64}, 3, Number{-262144}},
|
||||
{Number{64},
|
||||
11,
|
||||
Number{
|
||||
numberint(73786976294838206) * 1000 + 464,
|
||||
0,
|
||||
Number::normalized{}}},
|
||||
Number{false, 7378697629483820646ULL, 1, Number::normalized{}}},
|
||||
{Number{-64},
|
||||
11,
|
||||
-(Number{
|
||||
numberint(73786976294838206) * 1000 + 464,
|
||||
0,
|
||||
Number::normalized{}})}};
|
||||
Number{true, 7378697629483820646ULL, 1, Number::normalized{}}}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
BEAST_EXPECT((power(x, y) == z));
|
||||
}
|
||||
@@ -1267,13 +1309,17 @@ public:
|
||||
BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999);
|
||||
test(
|
||||
Number{
|
||||
maxMantissa * 1000 + 999, -3, Number::normalized()},
|
||||
"9999999999999999");
|
||||
test(
|
||||
-(Number{
|
||||
false,
|
||||
maxMantissa * 1000 + 999,
|
||||
-3,
|
||||
Number::normalized()}),
|
||||
Number::normalized()},
|
||||
"9999999999999999");
|
||||
test(
|
||||
Number{
|
||||
true,
|
||||
maxMantissa * 1000 + 999,
|
||||
-3,
|
||||
Number::normalized()},
|
||||
"-9999999999999999");
|
||||
|
||||
test(
|
||||
@@ -1296,18 +1342,18 @@ public:
|
||||
test(Number(-2, 11), "-2000000000000000000e-7");
|
||||
|
||||
test(Number::min(), "1000000000000000000e-32768");
|
||||
test(Number::max(), "9999999999999999999e32768");
|
||||
test(Number::lowest(), "-9999999999999999999e32768");
|
||||
test(Number::max(), "9223372036854775807e32768");
|
||||
test(Number::lowest(), "-9223372036854775807e32768");
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999'999ULL);
|
||||
test(
|
||||
Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
"9999999999999999990");
|
||||
test(
|
||||
-(Number{maxMantissa, 0, Number::normalized{}}),
|
||||
Number{true, maxMantissa, 0, Number::normalized{}},
|
||||
"-9999999999999999990");
|
||||
|
||||
test(
|
||||
@@ -1430,7 +1476,7 @@ public:
|
||||
(power(maxInt64, 2) == Number{85'070'591'730'234'62, 22}));
|
||||
|
||||
Number const max =
|
||||
Number{Number::maxMantissa(), 0, Number::normalized{}};
|
||||
Number{false, Number::maxMantissa(), 0, Number::normalized{}};
|
||||
BEAST_EXPECT(max.exponent() <= 0);
|
||||
// 99'999'999'999'999'980'000'000'000'000'001 - 32 digits
|
||||
BEAST_EXPECT((power(max, 2) == Number{99'999'999'999'999'98, 16}));
|
||||
@@ -1452,15 +1498,15 @@ public:
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
Number const max = Number{maxMantissa, 0, Number::normalized{}};
|
||||
Number const max =
|
||||
Number{false, maxMantissa, 0, Number::normalized{}};
|
||||
BEAST_EXPECT(max.mantissa() == maxMantissa / 10);
|
||||
BEAST_EXPECT(max.exponent() == 1);
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38
|
||||
// digits
|
||||
BEAST_EXPECT((
|
||||
power(max, 2) ==
|
||||
Number{
|
||||
maxMantissa / 10 - 1, 20, Number::normalized{}}));
|
||||
Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,8 +152,9 @@ struct STNumber_test : public beast::unit_test::suite
|
||||
numberFromJson(sfNumber, minInt) ==
|
||||
STNumber(
|
||||
sfNumber,
|
||||
-Number{
|
||||
numberint(9'223'372'036'854'775) * 1000 + 808,
|
||||
Number{
|
||||
true,
|
||||
9'223'372'036'854'775'808ULL,
|
||||
0,
|
||||
Number::normalized{}}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user