mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-05 16:57:56 +00:00
Compare commits
3 Commits
ximinez/le
...
ximinez/le
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21d66cbe78 | ||
|
|
3be46bc7d9 | ||
|
|
359622981e |
@@ -1,6 +1,8 @@
|
||||
#ifndef XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
#define XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#endif
|
||||
@@ -41,16 +43,17 @@ isPowerOfTen(T value)
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using numberuint128 = boost::multiprecision::uint128_t;
|
||||
using numberint128 = boost::multiprecision::int128_t;
|
||||
using numberuint = boost::multiprecision::uint128_t;
|
||||
using numberint = boost::multiprecision::int128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using numberuint128 = __uint128_t;
|
||||
using numberint128 = __int128_t;
|
||||
using numberuint = __uint128_t;
|
||||
using numberint = __int128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
struct MantissaRange
|
||||
{
|
||||
using internalrep = numberint128;
|
||||
using rep = std::int64_t;
|
||||
using internalrep = numberint;
|
||||
enum mantissa_scale { small, large };
|
||||
|
||||
explicit constexpr MantissaRange(mantissa_scale scale_, internalrep min_)
|
||||
@@ -61,7 +64,7 @@ struct MantissaRange
|
||||
{
|
||||
}
|
||||
|
||||
internalrep min;
|
||||
rep min;
|
||||
internalrep max;
|
||||
int log;
|
||||
mantissa_scale scale;
|
||||
@@ -69,11 +72,12 @@ struct MantissaRange
|
||||
|
||||
class Number
|
||||
{
|
||||
using uint128_t = numberuint128;
|
||||
using int128_t = numberint128;
|
||||
using uint128_t = numberuint;
|
||||
using int128_t = numberint;
|
||||
|
||||
using rep = std::int64_t;
|
||||
using rep = MantissaRange::rep;
|
||||
using internalrep = MantissaRange::internalrep;
|
||||
|
||||
internalrep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
@@ -88,16 +92,27 @@ public:
|
||||
explicit unchecked() = default;
|
||||
};
|
||||
|
||||
// Like unchecked, normalized is used with the ctors that take an
|
||||
// internalrep mantissa. Unlike unchecked, those ctors will normalize the
|
||||
// value.
|
||||
// Only unit tests are expected to use this class
|
||||
struct normalized
|
||||
{
|
||||
explicit normalized() = default;
|
||||
};
|
||||
|
||||
explicit constexpr Number() = default;
|
||||
|
||||
Number(rep mantissa);
|
||||
explicit Number(internalrep mantissa, int exponent);
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
explicit constexpr Number(
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept;
|
||||
// Only unit tests are expected to use this ctor
|
||||
explicit Number(internalrep mantissa, int exponent, normalized);
|
||||
|
||||
constexpr internalrep
|
||||
constexpr rep
|
||||
mantissa() const noexcept;
|
||||
constexpr int
|
||||
exponent() const noexcept;
|
||||
@@ -125,11 +140,11 @@ public:
|
||||
Number&
|
||||
operator/=(Number const& x);
|
||||
|
||||
static constexpr Number
|
||||
static Number
|
||||
min() noexcept;
|
||||
static constexpr Number
|
||||
static Number
|
||||
max() noexcept;
|
||||
static constexpr Number
|
||||
static Number
|
||||
lowest() noexcept;
|
||||
|
||||
/** Conversions to Number are implicit and conversions away from Number
|
||||
@@ -231,6 +246,15 @@ public:
|
||||
return os << to_string(x);
|
||||
}
|
||||
|
||||
friend std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
friend Number
|
||||
root(Number f, unsigned d);
|
||||
|
||||
friend Number
|
||||
root2(Number f);
|
||||
|
||||
// Thread local rounding control. Default is to_nearest
|
||||
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||
static rounding_mode
|
||||
@@ -244,7 +268,8 @@ public:
|
||||
static void
|
||||
setMantissaScale(MantissaRange::mantissa_scale scale);
|
||||
|
||||
inline static internalrep
|
||||
template <class T = rep>
|
||||
inline static T
|
||||
minMantissa()
|
||||
{
|
||||
return range_.get().min;
|
||||
@@ -283,21 +308,24 @@ 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 MantissaRange smallRange{
|
||||
MantissaRange::small,
|
||||
1'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(smallRange.min));
|
||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||
static_assert(smallRange.log == 15);
|
||||
// maxint64 9,223,372,036,854,775,808
|
||||
constexpr static MantissaRange largeRange{
|
||||
MantissaRange::large,
|
||||
1'000'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
// maxRep 9,223,372,036,854,775,808
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(largeRange.log == 18);
|
||||
static_assert(largeRange.min < std::numeric_limits<std::int64_t>::max());
|
||||
static_assert(largeRange.max > std::numeric_limits<std::int64_t>::max());
|
||||
static_assert(largeRange.min < maxRep);
|
||||
static_assert(largeRange.max > maxRep);
|
||||
|
||||
// The range for the mantissa when normalized.
|
||||
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
||||
@@ -314,9 +342,15 @@ private:
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa);
|
||||
|
||||
constexpr bool
|
||||
bool
|
||||
isnormal() const noexcept;
|
||||
|
||||
// Copy the number, but modify the exponent by "exponentDelta". Because the
|
||||
// mantissa doesn't change, the result will be "mostly" normalized, but the
|
||||
// exponent could go out of range, so it will be checked.
|
||||
Number
|
||||
shiftExponent(int exponentDelta) const;
|
||||
|
||||
class Guard;
|
||||
};
|
||||
|
||||
@@ -328,8 +362,14 @@ inline constexpr Number::Number(
|
||||
{
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent)
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
inline Number::Number(internalrep mantissa, int exponent, normalized)
|
||||
: Number(mantissa, exponent, unchecked{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa, int exponent)
|
||||
: Number(mantissa, exponent, normalized{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
@@ -338,16 +378,36 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
{
|
||||
}
|
||||
|
||||
inline constexpr Number::internalrep
|
||||
inline constexpr Number::rep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
return mantissa_;
|
||||
auto m = mantissa_;
|
||||
while (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || m % 10 == 0,
|
||||
"ripple::Number::mantissa",
|
||||
"large normalized mantissa has no remainder");
|
||||
m /= 10;
|
||||
}
|
||||
return static_cast<Number::rep>(m);
|
||||
}
|
||||
|
||||
inline constexpr int
|
||||
Number::exponent() const noexcept
|
||||
{
|
||||
return exponent_;
|
||||
auto m = mantissa_;
|
||||
auto e = exponent_;
|
||||
while (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || m % 10 == 0,
|
||||
"ripple::Number::exponent",
|
||||
"large normalized mantissa has no remainder");
|
||||
m /= 10;
|
||||
++e;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
@@ -432,30 +492,31 @@ operator/(Number const& x, Number const& y)
|
||||
return z;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
inline Number
|
||||
Number::min() noexcept
|
||||
{
|
||||
return Number{range_.get().min, minExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
inline Number
|
||||
Number::max() noexcept
|
||||
{
|
||||
return Number{range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
inline Number
|
||||
Number::lowest() noexcept
|
||||
{
|
||||
return -Number{range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr bool
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ 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::numberuint128>);
|
||||
static_assert(std::is_same_v<uint128_t, ripple::numberuint>);
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct make_unsigned<ripple::numberint128>
|
||||
struct make_unsigned<ripple::numberint>
|
||||
{
|
||||
using type = uint128_t;
|
||||
};
|
||||
@@ -256,13 +256,37 @@ Number::normalize(
|
||||
m /= 10;
|
||||
++exponent_;
|
||||
}
|
||||
mantissa_ = m;
|
||||
if ((exponent_ < minExponent) || (mantissa_ < minMantissa))
|
||||
if ((exponent_ < minExponent) || (m < minMantissa))
|
||||
{
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
return;
|
||||
}
|
||||
// When using the largeRange, "m" needs fit within an int64, even if
|
||||
// the final mantissa_ is going to end up larger to fit within the range.
|
||||
// Cut it down here so that the rounding will be done while it's smaller.
|
||||
//
|
||||
// Example: 9,900,000,000,000,555,555 > 9,223,372,036,854,775,808,
|
||||
// so "m" will be modified to 990,000,000,000,055,555. Then that value
|
||||
// will be rounded to 990,000,000,000,055,555 or
|
||||
// 990,000,000,000,055,556, depending on the rounding mode. Finally,
|
||||
// mantissa_ will be m*10 so it fits within the range, and end up as
|
||||
// 9,900,000,000,000,555,550 or 9,900,000,000,000,555,560.
|
||||
// mantissa() will return mantissa_ / 10, and exponent() will return
|
||||
// exponent_ + 1.
|
||||
if (m > maxRep)
|
||||
{
|
||||
if (exponent_ >= maxExponent)
|
||||
throw std::overflow_error("Number::normalize 1.5");
|
||||
g.push(m % 10);
|
||||
m /= 10;
|
||||
++exponent_;
|
||||
}
|
||||
XRPL_ASSERT_PARTS(
|
||||
m <= maxRep,
|
||||
"ripple::Number::normalize",
|
||||
"intermediate mantissa fits in int64");
|
||||
mantissa_ = m;
|
||||
|
||||
auto r = g.round();
|
||||
if (r == 1 || (r == 0 && (mantissa_ & 1) == 1))
|
||||
@@ -276,6 +300,18 @@ Number::normalize(
|
||||
}
|
||||
if (exponent_ > maxExponent)
|
||||
throw std::overflow_error("Number::normalize 2");
|
||||
if (mantissa_ < minMantissa)
|
||||
{
|
||||
// When using the largeRange, the intermediate "m" needs fit within an
|
||||
// int64, but mantissa_ needs to fit within the minMantissa /
|
||||
// maxMantissa range.
|
||||
mantissa_ *= 10;
|
||||
--exponent_;
|
||||
}
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa_ >= minMantissa && mantissa_ <= maxMantissa,
|
||||
"ripple::Number::normalize",
|
||||
"final mantissa fits in range");
|
||||
|
||||
if (negative)
|
||||
mantissa_ = -mantissa_;
|
||||
@@ -288,6 +324,29 @@ Number::normalize()
|
||||
mantissa_, exponent_, Number::minMantissa(), Number::maxMantissa());
|
||||
}
|
||||
|
||||
// Copy the number, but set a new exponent. Because the mantissa doesn't change,
|
||||
// the result will be "mostly" normalized, but the exponent could go out of
|
||||
// range.
|
||||
Number
|
||||
Number::shiftExponent(int exponentDelta) const
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
isnormal(), "ripple::Number::shiftExponent", "normalized");
|
||||
auto const newExponent = exponent_ + exponentDelta;
|
||||
if (newExponent >= maxExponent)
|
||||
throw std::overflow_error("Number::shiftExponent");
|
||||
if (newExponent < minExponent)
|
||||
{
|
||||
return Number{};
|
||||
}
|
||||
Number const result{mantissa_, newExponent, unchecked{}};
|
||||
XRPL_ASSERT_PARTS(
|
||||
result.isnormal(),
|
||||
"ripple::Number::shiftExponent",
|
||||
"result is normalized");
|
||||
return result;
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator+=(Number const& y)
|
||||
{
|
||||
@@ -306,16 +365,16 @@ Number::operator+=(Number const& y)
|
||||
XRPL_ASSERT(
|
||||
isnormal() && y.isnormal(),
|
||||
"ripple::Number::operator+=(Number) : is normal");
|
||||
auto xm = mantissa();
|
||||
auto xe = exponent();
|
||||
auto xm = mantissa_;
|
||||
auto xe = exponent_;
|
||||
int xn = 1;
|
||||
if (xm < 0)
|
||||
{
|
||||
xm = -xm;
|
||||
xn = -1;
|
||||
}
|
||||
auto ym = y.mantissa();
|
||||
auto ye = y.exponent();
|
||||
auto ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
int yn = 1;
|
||||
if (ym < 0)
|
||||
{
|
||||
@@ -407,6 +466,10 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
mantissa_ = xm * xn;
|
||||
exponent_ = xe;
|
||||
normalize();
|
||||
XRPL_ASSERT(
|
||||
isnormal() || *this == Number{},
|
||||
"ripple::Number::operator+=(Number) : result is normal");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -451,16 +514,16 @@ Number::operator*=(Number const& y)
|
||||
XRPL_ASSERT(
|
||||
isnormal() && y.isnormal(),
|
||||
"ripple::Number::operator*=(Number) : is normal");
|
||||
auto xm = mantissa();
|
||||
auto xe = exponent();
|
||||
auto xm = mantissa_;
|
||||
auto xe = exponent_;
|
||||
int xn = 1;
|
||||
if (xm < 0)
|
||||
{
|
||||
xm = -xm;
|
||||
xn = -1;
|
||||
}
|
||||
auto ym = y.mantissa();
|
||||
auto ye = y.exponent();
|
||||
auto ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
int yn = 1;
|
||||
if (ym < 0)
|
||||
{
|
||||
@@ -507,6 +570,7 @@ Number::operator*=(Number const& y)
|
||||
std::to_string(xe));
|
||||
mantissa_ = xm * zn;
|
||||
exponent_ = xe;
|
||||
normalize();
|
||||
XRPL_ASSERT(
|
||||
isnormal() || *this == Number{},
|
||||
"ripple::Number::operator*=(Number) : result is normal");
|
||||
@@ -521,16 +585,16 @@ Number::operator/=(Number const& y)
|
||||
if (*this == Number{})
|
||||
return *this;
|
||||
int np = 1;
|
||||
auto nm = mantissa();
|
||||
auto ne = exponent();
|
||||
auto nm = mantissa_;
|
||||
auto ne = exponent_;
|
||||
if (nm < 0)
|
||||
{
|
||||
nm = -nm;
|
||||
np = -1;
|
||||
}
|
||||
int dp = 1;
|
||||
auto dm = y.mantissa();
|
||||
auto de = y.exponent();
|
||||
auto dm = y.mantissa_;
|
||||
auto de = y.exponent_;
|
||||
if (dm < 0)
|
||||
{
|
||||
dm = -dm;
|
||||
@@ -548,7 +612,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() * 10,
|
||||
f >= Number::minMantissa<internalrep>() * 10,
|
||||
"Number::operator/=",
|
||||
"factor expected size");
|
||||
|
||||
@@ -649,12 +713,12 @@ to_string(Number const& amount)
|
||||
if (amount == Number{})
|
||||
return "0";
|
||||
|
||||
auto const exponent = amount.exponent();
|
||||
auto const exponent = amount.exponent_;
|
||||
|
||||
bool const negative = amount.mantissa() < 0;
|
||||
bool const negative = amount.mantissa_ < 0;
|
||||
|
||||
auto const mantissa = [&]() {
|
||||
auto mantissa = amount.mantissa();
|
||||
auto mantissa = amount.mantissa_;
|
||||
if (negative)
|
||||
{
|
||||
mantissa = -mantissa;
|
||||
@@ -806,7 +870,9 @@ root(Number f, unsigned d)
|
||||
return di - k2;
|
||||
}();
|
||||
e += ex;
|
||||
f = Number{f.mantissa(), f.exponent() - e}; // f /= 10^e;
|
||||
f = f.shiftExponent(-e); // f /= 10^e;
|
||||
XRPL_ASSERT_PARTS(
|
||||
f.isnormal(), "ripple::root(Number, unsigned)", "f is normalized");
|
||||
bool neg = false;
|
||||
if (f < Number{})
|
||||
{
|
||||
@@ -838,7 +904,12 @@ root(Number f, unsigned d)
|
||||
} while (r != rm1 && r != rm2);
|
||||
|
||||
// return r * 10^(e/d) to reverse scaling
|
||||
return Number{r.mantissa(), r.exponent() + e / di};
|
||||
auto const result = r.shiftExponent(e / di);
|
||||
XRPL_ASSERT_PARTS(
|
||||
result.isnormal(),
|
||||
"ripple::root(Number, unsigned)",
|
||||
"result is normalized");
|
||||
return result;
|
||||
}
|
||||
|
||||
Number
|
||||
@@ -857,7 +928,8 @@ root2(Number f)
|
||||
auto e = f.exponent() + Number::mantissaLog() + 1;
|
||||
if (e % 2 != 0)
|
||||
++e;
|
||||
f = Number{f.mantissa(), f.exponent() - e}; // f /= 10^e;
|
||||
f = f.shiftExponent(-e); // f /= 10^e;
|
||||
XRPL_ASSERT_PARTS(f.isnormal(), "ripple::root2(Number)", "f is normalized");
|
||||
|
||||
// Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
|
||||
auto const D = 105;
|
||||
@@ -878,7 +950,11 @@ root2(Number f)
|
||||
} while (r != rm1 && r != rm2);
|
||||
|
||||
// return r * 10^(e/2) to reverse scaling
|
||||
return Number{r.mantissa(), r.exponent() + e / 2};
|
||||
auto const result = r.shiftExponent(e / 2);
|
||||
XRPL_ASSERT_PARTS(
|
||||
result.isnormal(), "ripple::root2(Number)", "result is normalized");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns f^(n/d)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
//
|
||||
// Do not remove. Forces IOUAmount.h to stay first, to verify it can compile
|
||||
// without any hidden dependencies
|
||||
#include <xrpl/basics/LocalValue.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
//
|
||||
// Do not remove. Forces Rules.h to stay first, to verify it can compile
|
||||
// without any hidden dependencies
|
||||
#include <xrpl/basics/LocalValue.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
@@ -50,27 +50,16 @@ STNumber::add(Serializer& s) const
|
||||
XRPL_ASSERT(
|
||||
getFName().fieldType == getSType(),
|
||||
"ripple::STNumber::add : field type match");
|
||||
if (value_.mantissa() <= std::numeric_limits<std::int64_t>::max() &&
|
||||
value_.mantissa() >= std::numeric_limits<std::int64_t>::min())
|
||||
{
|
||||
// If the mantissa fits in the range of std::int64_t, write it directly.
|
||||
// This preserves the maximum available precision.
|
||||
// With the small range, all numbers should be written this way. With
|
||||
// the large range, it's likely that most numbers will be written this
|
||||
// way.
|
||||
s.add64(static_cast<std::int64_t>(value_.mantissa()));
|
||||
s.add32(value_.exponent());
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr std::int64_t min = 100'000'000'000'000'000LL;
|
||||
constexpr std::int64_t max = min * 10 - 1;
|
||||
static_assert(
|
||||
min < (std::numeric_limits<std::int64_t>::max() - 1 / 10));
|
||||
auto const [mantissa, exponent] = value_.normalizeToRange(min, max);
|
||||
s.add64(mantissa);
|
||||
s.add32(exponent);
|
||||
}
|
||||
|
||||
auto const mantissa = value_.mantissa();
|
||||
auto const exponent = value_.exponent();
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa <= std::numeric_limits<std::int64_t>::max() &&
|
||||
mantissa >= std::numeric_limits<std::int64_t>::min(),
|
||||
"ripple::STNumber::add",
|
||||
"mantissa in valid range");
|
||||
s.add64(mantissa);
|
||||
s.add32(exponent);
|
||||
}
|
||||
|
||||
Number const&
|
||||
@@ -202,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<numberint128>::max() >
|
||||
std::numeric_limits<numberint>::max() >=
|
||||
std::numeric_limits<decltype(parts.mantissa)>::max());
|
||||
}
|
||||
else
|
||||
@@ -210,11 +199,11 @@ numberFromJson(SField const& field, Json::Value const& value)
|
||||
Throw<std::runtime_error>("not a number");
|
||||
}
|
||||
|
||||
numberint128 mantissa = parts.mantissa;
|
||||
numberint mantissa = parts.mantissa;
|
||||
if (parts.negative)
|
||||
mantissa = -mantissa;
|
||||
|
||||
return STNumber{field, Number{mantissa, parts.exponent}};
|
||||
return STNumber{field, Number{mantissa, parts.exponent, Number::normalized{}}};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -34,43 +34,52 @@ public:
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "test_limits " << to_string(scale);
|
||||
bool caught = false;
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
auto const minMantissa = Number::minMantissa<numberint>();
|
||||
try
|
||||
{
|
||||
Number x{minMantissa * 10, 32768};
|
||||
Number x = Number{minMantissa * 10, 32768, Number::normalized{}};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
Number x{minMantissa * 10, 32767};
|
||||
BEAST_EXPECT((x == Number{minMantissa, 32768}));
|
||||
Number z{minMantissa, -32769};
|
||||
BEAST_EXPECT(z == Number{});
|
||||
Number y{minMantissa * 1'000 + 1'500, 32000};
|
||||
BEAST_EXPECT((y == Number{minMantissa + 2, 32003}));
|
||||
Number m{std::numeric_limits<std::int64_t>::min()};
|
||||
|
||||
auto test = [this](auto const& x, auto const& y) {
|
||||
auto const result = x == y;
|
||||
std::stringstream ss;
|
||||
ss << x << " == " << y << " -> " << (result ? "true" : "false");
|
||||
BEAST_EXPECTS(result, ss.str());
|
||||
};
|
||||
|
||||
test(
|
||||
Number{minMantissa * 10, 32767, Number::normalized{}},
|
||||
Number{minMantissa, 32768, Number::normalized{}});
|
||||
test(Number{minMantissa, -32769, Number::normalized{}}, Number{});
|
||||
test(
|
||||
Number{minMantissa * 1'000 + 1'500, 32000, Number::normalized{}},
|
||||
Number{minMantissa + 2, 32003, Number::normalized{}});
|
||||
// 9,223,372,036,854,775,808
|
||||
BEAST_EXPECT(
|
||||
(m ==
|
||||
Number{
|
||||
scale == MantissaRange::small
|
||||
? -9'223'372'036'854'776
|
||||
: std::numeric_limits<std::int64_t>::min(),
|
||||
18 - Number::mantissaLog()}));
|
||||
Number M{std::numeric_limits<std::int64_t>::max()};
|
||||
BEAST_EXPECT(
|
||||
(M ==
|
||||
Number{
|
||||
scale == MantissaRange::small
|
||||
? 9'223'372'036'854'776
|
||||
: std::numeric_limits<std::int64_t>::max(),
|
||||
18 - Number::mantissaLog()}));
|
||||
|
||||
test(
|
||||
Number{std::numeric_limits<std::int64_t>::min()},
|
||||
Number{
|
||||
scale == MantissaRange::small
|
||||
? -9'223'372'036'854'776
|
||||
: std::numeric_limits<std::int64_t>::min(),
|
||||
18 - Number::mantissaLog()});
|
||||
test(
|
||||
Number{std::numeric_limits<std::int64_t>::max()},
|
||||
Number{
|
||||
scale == MantissaRange::small
|
||||
? 9'223'372'036'854'776
|
||||
: std::numeric_limits<std::int64_t>::max(),
|
||||
18 - Number::mantissaLog()});
|
||||
caught = false;
|
||||
try
|
||||
{
|
||||
Number q{minMantissa * 100 - 1, 32767};
|
||||
Number q =
|
||||
Number{minMantissa * 100 - 1, 32767, Number::normalized{}};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -79,92 +88,6 @@ public:
|
||||
BEAST_EXPECT(caught);
|
||||
}
|
||||
|
||||
void
|
||||
testToString()
|
||||
{
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "testToString " << to_string(scale);
|
||||
|
||||
auto test = [this](Number const& n, std::string const& expected) {
|
||||
auto const result = to_string(n);
|
||||
std::stringstream ss;
|
||||
ss << "to_string(" << result << "). Expected: " << expected;
|
||||
BEAST_EXPECTS(result == expected, ss.str());
|
||||
};
|
||||
|
||||
test(Number(-2, 0), "-2");
|
||||
test(Number(0, 0), "0");
|
||||
test(Number(2, 0), "2");
|
||||
test(Number(25, -3), "0.025");
|
||||
test(Number(-25, -3), "-0.025");
|
||||
test(Number(25, 1), "250");
|
||||
test(Number(-25, 1), "-250");
|
||||
switch (scale)
|
||||
{
|
||||
case MantissaRange::small:
|
||||
test(Number(2, 20), "2000000000000000e5");
|
||||
test(Number(-2, -20), "-2000000000000000e-35");
|
||||
// Test the edges
|
||||
// ((exponent < -(25)) || (exponent > -(5)))))
|
||||
test(Number(2, -10), "0.0000000002");
|
||||
test(Number(2, -11), "2000000000000000e-26");
|
||||
|
||||
test(Number(-2, 10), "-20000000000");
|
||||
test(Number(-2, 11), "-2000000000000000e-4");
|
||||
|
||||
test(Number::min(), "1000000000000000e-32768");
|
||||
test(Number::max(), "9999999999999999e32768");
|
||||
test(Number::lowest(), "-9999999999999999e32768");
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
test(
|
||||
Number{
|
||||
numberuint128(9'999'999'999'999'999) * 1000 + 999,
|
||||
-3},
|
||||
"9999999999999999");
|
||||
test(
|
||||
-(Number{
|
||||
numberuint128(9'999'999'999'999'999) * 1000 + 999,
|
||||
-3}),
|
||||
"-9999999999999999");
|
||||
}
|
||||
break;
|
||||
case MantissaRange::large:
|
||||
test(Number(2, 20), "2000000000000000000e2");
|
||||
test(Number(-2, -20), "-2000000000000000000e-38");
|
||||
// Test the edges
|
||||
// ((exponent < -(28)) || (exponent > -(8)))))
|
||||
test(Number(2, -10), "0.0000000002");
|
||||
test(Number(2, -11), "2000000000000000000e-29");
|
||||
|
||||
test(Number(-2, 10), "-20000000000");
|
||||
test(Number(-2, 11), "-2000000000000000000e-7");
|
||||
|
||||
test(Number::min(), "1000000000000000000e-32768");
|
||||
test(Number::max(), "9999999999999999999e32768");
|
||||
test(Number::lowest(), "-9999999999999999999e32768");
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
test(
|
||||
Number{
|
||||
numberuint128(9'999'999'999'999'999) * 1000 + 999,
|
||||
0},
|
||||
"9999999999999999999");
|
||||
test(
|
||||
-(Number{
|
||||
numberuint128(9'999'999'999'999'999) * 1000 + 999,
|
||||
0}),
|
||||
"-9999999999999999999");
|
||||
}
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_add()
|
||||
{
|
||||
@@ -206,10 +129,15 @@ public:
|
||||
{Number{-1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{
|
||||
-(numberint128(9'999'999'999'999'344) * 1'000 + 444), -19}},
|
||||
-(numberint(9'999'999'999'999'344) * 1'000 + 444),
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{-6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{numberint128(9'999'999'999'999'344) * 1'000 + 444, -19}},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'344) * 1'000 + 444,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{}, Number{5}, Number{5}},
|
||||
{Number{5}, Number{}, Number{5}},
|
||||
{Number{5'555'555'555'555'555'000, -32768},
|
||||
@@ -228,17 +156,28 @@ public:
|
||||
{Number{-1'000'000'000'000'000'000, -18},
|
||||
Number{6'555'555'555'555'555'555, -35},
|
||||
Number{
|
||||
-(numberint128(9'999'999'999'999'999) * 1'000 + 344), -19}},
|
||||
-(numberint(9'999'999'999'999'999) * 1'000 + 344),
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{-6'555'555'555'555'555'555, -35},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{numberint128(9'999'999'999'999'999) * 1'000 + 344, -19}},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1'000 + 344,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{}, Number{5}, Number{5}},
|
||||
{Number{5'555'555'555'555'555'555, -32768},
|
||||
Number{-5'555'555'555'555'555'554, -32768},
|
||||
Number{0}},
|
||||
{Number{-(numberint128(9'999'999'999'999'999) * 1'000 + 999), -37},
|
||||
{Number{
|
||||
-(numberint(9'999'999'999'999'999) * 1'000 + 999),
|
||||
-37,
|
||||
Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{numberint128(9'999'999'999'999'999) * 1'000 + 990, -19}}});
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1'000 + 990,
|
||||
-19,
|
||||
Number::normalized{}}}});
|
||||
auto test = [this](auto const& c) {
|
||||
for (auto const& [x, y, z] : c)
|
||||
{
|
||||
@@ -256,27 +195,8 @@ public:
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
if (scale == MantissaRange::small)
|
||||
Number{9'999'999'999'999'999, 32768} +
|
||||
Number{5'000'000'000'000'000, 32767};
|
||||
else
|
||||
Number{numberint128(9'999'999'999'999'999) * 1'000, 32768} +
|
||||
Number{5'000'000'000'000'000'000, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
}
|
||||
if (scale != MantissaRange::small)
|
||||
{
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{
|
||||
numberuint128(9'999'999'999'999'999) * 1000 + 999, 32768} +
|
||||
Number{5'000'000'000'000'000'000, 32767};
|
||||
Number{Number::maxMantissa(), 32768, Number::normalized{}} +
|
||||
Number{Number::minMantissa() * 5, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -315,11 +235,16 @@ public:
|
||||
// with larger mantissa
|
||||
{{Number{1'000'000'000'000'000, -15},
|
||||
Number{6'555'555'555'555'555, -29},
|
||||
Number{numberint128(9'999'999'999'999'344) * 1'000 + 444, -19}},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'344) * 1'000 + 444,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{6'555'555'555'555'555, -29},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{
|
||||
-(numberint128(9'999'999'999'999'344) * 1'000 + 444), -19}},
|
||||
-(numberint(9'999'999'999'999'344) * 1'000 + 444),
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{1'000'000'000'000'000, -15},
|
||||
Number{1'000'000'000'000'000, -15},
|
||||
Number{0}},
|
||||
@@ -332,11 +257,16 @@ public:
|
||||
// Items from cSmall expanded for the larger mantissa
|
||||
{Number{1'000'000'000'000'000'000, -18},
|
||||
Number{6'555'555'555'555'555'555, -32},
|
||||
Number{numberint128(9'999'999'999'999'344) * 1'000 + 444, -19}},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'344) * 1'000 + 444,
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{6'555'555'555'555'555'555, -32},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{
|
||||
-(numberint128(9'999'999'999'999'344) * 1'000 + 444), -19}},
|
||||
-(numberint(9'999'999'999'999'344) * 1'000 + 444),
|
||||
-19,
|
||||
Number::normalized{}}},
|
||||
{Number{1'000'000'000'000'000'000, -18},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{0}},
|
||||
@@ -383,6 +313,9 @@ public:
|
||||
else
|
||||
test(cLarge);
|
||||
};
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
auto const maxInt64 = std::numeric_limits<std::int64_t>::max();
|
||||
|
||||
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
||||
{
|
||||
auto const cSmall = std::to_array<Case>({
|
||||
@@ -423,7 +356,10 @@ public:
|
||||
Number{1999999999999999862, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{numberint128(9'999'999'999'999'999) * 1000 + 579, -18}},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1000 + 579,
|
||||
-18,
|
||||
Number::normalized{}}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
@@ -442,10 +378,14 @@ public:
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0}},
|
||||
// Maximum mantissa range
|
||||
{Number{numberint128(9'999'999'999'999'999) * 1000 + 999, 0},
|
||||
Number{numberint128(9'999'999'999'999'999) * 1000 + 999, 0},
|
||||
Number{numberint128(9'999'999'999'999'999) * 1000 + 998, 19}},
|
||||
// Maximum mantissa range - rounds up to 1e19
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38}},
|
||||
// Maximum int64 range
|
||||
{Number{maxInt64, 0},
|
||||
Number{maxInt64, 0},
|
||||
Number{85'070'591'730'234'615'85, 19}},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
@@ -474,37 +414,52 @@ public:
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{numberint128(9999999999999999) * 1000 + 579, -18}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
// Items from cSmall expanded for the larger mantissa, except
|
||||
// duplicates.
|
||||
// Sadly, it looks like sqrt(2)^2 != 2 with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999997, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0}}});
|
||||
{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{
|
||||
numberint(9999999999999999) * 1000 + 579,
|
||||
-18,
|
||||
Number::normalized{}}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999997, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
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{}}},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{maxInt64, 0},
|
||||
Number{maxInt64, 0},
|
||||
Number{85'070'591'730'234'615'84, 19}},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
Number::setround(Number::downward);
|
||||
@@ -532,38 +487,52 @@ public:
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999862, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{
|
||||
numberint128(9'999'999'999'999'999) * 1000 + 579, -18}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
// Items from cSmall expanded for the larger mantissa, except
|
||||
// duplicates.
|
||||
// Sadly, it looks like sqrt(2)^2 != 2 with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999998, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0}}});
|
||||
{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999862, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{
|
||||
numberint(9'999'999'999'999'999) * 1000 + 579,
|
||||
-18,
|
||||
Number::normalized{}}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999998, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
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{}}},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{maxInt64, 0},
|
||||
Number{maxInt64, 0},
|
||||
Number{85'070'591'730'234'615'84, 19}},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
Number::setround(Number::upward);
|
||||
@@ -591,37 +560,49 @@ public:
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{999999999999999958, -17}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2000000000000000001, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999997, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{1000000000000000001, -17}}});
|
||||
{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999861, -18}},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{999999999999999958, -17}},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2000000000000000001, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999997, -18}},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
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{1, 38}},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{maxInt64, 0},
|
||||
Number{maxInt64, 0},
|
||||
Number{85'070'591'730'234'615'85, 19}},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_mul " << to_string(Number::getMantissaScale())
|
||||
@@ -630,27 +611,8 @@ public:
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
if (scale == MantissaRange::small)
|
||||
Number{9'999'999'999'999'999, 32768} *
|
||||
Number{5'000'000'000'000'000, 32767};
|
||||
else
|
||||
Number{numberint128(9'999'999'999'999'999) * 1'000, 32768} *
|
||||
Number{5'000'000'000'000'000'000, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
caught = true;
|
||||
}
|
||||
BEAST_EXPECT(caught);
|
||||
}
|
||||
if (scale != MantissaRange::small)
|
||||
{
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number{
|
||||
numberint128(9'999'999'999'999'999) * 1000 + 999, 32768} +
|
||||
Number{5'000'000'000'000'000'000, 32767};
|
||||
Number{maxMantissa, 32768, Number::normalized{}} *
|
||||
Number{Number::minMantissa() * 5, 32767};
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
@@ -676,6 +638,7 @@ public:
|
||||
BEAST_EXPECTS(result == z, ss.str());
|
||||
}
|
||||
};
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
auto tests = [&](auto const& cSmall, auto const& cLarge) {
|
||||
if (scale == MantissaRange::small)
|
||||
test(cSmall);
|
||||
@@ -722,11 +685,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{
|
||||
numberint128(9'999'999'999'999'999) * 1'000 + 999,
|
||||
-18}}});
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -771,11 +732,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{
|
||||
numberint128(9'999'999'999'999'999) * 1'000 + 999,
|
||||
-18}}});
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -820,11 +779,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{
|
||||
numberint128(9'999'999'999'999'999) * 1'000 + 999,
|
||||
-18}}});
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -869,11 +826,9 @@ public:
|
||||
{Number{1414213562373095049, -13},
|
||||
Number{1414213562373095049, -13},
|
||||
Number{1}},
|
||||
{Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0},
|
||||
{Number{maxMantissa, 0, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000},
|
||||
Number{
|
||||
numberint128(9'999'999'999'999'999) * 1'000 + 999,
|
||||
-18}}});
|
||||
Number{maxMantissa, -18, Number::normalized{}}}});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
testcase << "test_div " << to_string(Number::getMantissaScale())
|
||||
@@ -1002,10 +957,16 @@ public:
|
||||
{Number{-64}, 3, Number{-262144}},
|
||||
{Number{64},
|
||||
11,
|
||||
Number{numberint128(73786976294838206) * 1000 + 464, 0}},
|
||||
Number{
|
||||
numberint(73786976294838206) * 1000 + 464,
|
||||
0,
|
||||
Number::normalized{}}},
|
||||
{Number{-64},
|
||||
11,
|
||||
Number{-(numberint128(73786976294838206) * 1000 + 464), 0}}};
|
||||
-(Number{
|
||||
numberint(73786976294838206) * 1000 + 464,
|
||||
0,
|
||||
Number::normalized{}})}};
|
||||
for (auto const& [x, y, z] : c)
|
||||
BEAST_EXPECT((power(x, y) == z));
|
||||
}
|
||||
@@ -1263,6 +1224,105 @@ public:
|
||||
BEAST_EXPECT((squelch(Number{-9, -7}, limit) == Number{0}));
|
||||
}
|
||||
|
||||
void
|
||||
testToString()
|
||||
{
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "testToString " << to_string(scale);
|
||||
|
||||
auto test = [this](Number const& n, std::string const& expected) {
|
||||
auto const result = to_string(n);
|
||||
std::stringstream ss;
|
||||
ss << "to_string(" << result << "). Expected: " << expected;
|
||||
BEAST_EXPECTS(result == expected, ss.str());
|
||||
};
|
||||
|
||||
test(Number(-2, 0), "-2");
|
||||
test(Number(0, 0), "0");
|
||||
test(Number(2, 0), "2");
|
||||
test(Number(25, -3), "0.025");
|
||||
test(Number(-25, -3), "-0.025");
|
||||
test(Number(25, 1), "250");
|
||||
test(Number(-25, 1), "-250");
|
||||
switch (scale)
|
||||
{
|
||||
case MantissaRange::small:
|
||||
test(Number(2, 20), "2000000000000000e5");
|
||||
test(Number(-2, -20), "-2000000000000000e-35");
|
||||
// Test the edges
|
||||
// ((exponent < -(25)) || (exponent > -(5)))))
|
||||
test(Number(2, -10), "0.0000000002");
|
||||
test(Number(2, -11), "2000000000000000e-26");
|
||||
|
||||
test(Number(-2, 10), "-20000000000");
|
||||
test(Number(-2, 11), "-2000000000000000e-4");
|
||||
|
||||
test(Number::min(), "1000000000000000e-32768");
|
||||
test(Number::max(), "9999999999999999e32768");
|
||||
test(Number::lowest(), "-9999999999999999e32768");
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999);
|
||||
test(
|
||||
Number{
|
||||
maxMantissa * 1000 + 999, -3, Number::normalized()},
|
||||
"9999999999999999");
|
||||
test(
|
||||
-(Number{
|
||||
maxMantissa * 1000 + 999,
|
||||
-3,
|
||||
Number::normalized()}),
|
||||
"-9999999999999999");
|
||||
|
||||
test(
|
||||
Number{std::numeric_limits<std::int64_t>::max(), -3},
|
||||
"9223372036854775");
|
||||
test(
|
||||
-(Number{std::numeric_limits<std::int64_t>::max(), -3}),
|
||||
"-9223372036854775");
|
||||
}
|
||||
break;
|
||||
case MantissaRange::large:
|
||||
test(Number(2, 20), "2000000000000000000e2");
|
||||
test(Number(-2, -20), "-2000000000000000000e-38");
|
||||
// Test the edges
|
||||
// ((exponent < -(28)) || (exponent > -(8)))))
|
||||
test(Number(2, -10), "0.0000000002");
|
||||
test(Number(2, -11), "2000000000000000000e-29");
|
||||
|
||||
test(Number(-2, 10), "-20000000000");
|
||||
test(Number(-2, 11), "-2000000000000000000e-7");
|
||||
|
||||
test(Number::min(), "1000000000000000000e-32768");
|
||||
test(Number::max(), "9999999999999999999e32768");
|
||||
test(Number::lowest(), "-9999999999999999999e32768");
|
||||
{
|
||||
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{}},
|
||||
"9999999999999999990");
|
||||
test(
|
||||
-(Number{maxMantissa, 0, Number::normalized{}}),
|
||||
"-9999999999999999990");
|
||||
|
||||
test(
|
||||
Number{std::numeric_limits<std::int64_t>::max(), 0},
|
||||
"9223372036854775807");
|
||||
test(
|
||||
-(Number{std::numeric_limits<std::int64_t>::max(), 0}),
|
||||
"-9223372036854775807");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_relationals()
|
||||
{
|
||||
@@ -1369,7 +1429,8 @@ public:
|
||||
BEAST_EXPECT(
|
||||
(power(maxInt64, 2) == Number{85'070'591'730'234'62, 22}));
|
||||
|
||||
Number const max = Number{Number::maxMantissa(), 0};
|
||||
Number const max =
|
||||
Number{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}));
|
||||
@@ -1388,12 +1449,18 @@ public:
|
||||
BEAST_EXPECT(
|
||||
(power(maxInt64, 2) == Number{85'070'591'730'234'615'85, 19}));
|
||||
|
||||
Number const max = Number{Number::maxMantissa(), 0};
|
||||
BEAST_EXPECT(max.exponent() <= 0);
|
||||
// 99999999999999999980000000000000000001 - also 38 digits
|
||||
BEAST_EXPECT(
|
||||
(power(max, 2) ==
|
||||
Number{numberint128(9'999'999'999'999'999) * 1000 + 998, 19}));
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
Number const max = Number{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{}}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,9 +153,9 @@ struct STNumber_test : public beast::unit_test::suite
|
||||
STNumber(
|
||||
sfNumber,
|
||||
-Number{
|
||||
numberint128(9'223'372'036'854'775) * 1000 +
|
||||
808,
|
||||
0}));
|
||||
numberint(9'223'372'036'854'775) * 1000 + 808,
|
||||
0,
|
||||
Number::normalized{}}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user