diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index c3738db286..5e2989823c 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -1,6 +1,8 @@ #ifndef XRPL_BASICS_NUMBER_H_INCLUDED #define XRPL_BASICS_NUMBER_H_INCLUDED +#include + #ifdef _MSC_VER #include #endif @@ -41,16 +43,16 @@ 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 internalrep = numberint; enum mantissa_scale { small, large }; explicit constexpr MantissaRange(mantissa_scale scale_, internalrep min_) @@ -69,11 +71,14 @@ struct MantissaRange class Number { - using uint128_t = numberuint128; - using int128_t = numberint128; + using uint128_t = numberuint; + using int128_t = numberint; +public: using rep = std::int64_t; using internalrep = MantissaRange::internalrep; + +private: internalrep mantissa_{0}; int exponent_{std::numeric_limits::lowest()}; @@ -92,12 +97,9 @@ public: Number(rep mantissa); explicit Number(internalrep mantissa, int exponent); - explicit constexpr Number( - internalrep mantissa, - int exponent, - unchecked) noexcept; + explicit constexpr Number(internalrep mantissa, int exponent, unchecked) noexcept; - constexpr internalrep + constexpr rep mantissa() const noexcept; constexpr int exponent() const noexcept; @@ -125,11 +127,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 +233,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 @@ -283,21 +294,24 @@ private: static thread_local rounding_mode mode_; // The available ranges for mantissa + constexpr static internalrep maxRep = + std::numeric_limits::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::max()); - static_assert(largeRange.max > std::numeric_limits::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,16 +328,13 @@ private: internalrep const& minMantissa, internalrep const& maxMantissa); - constexpr bool + bool isnormal() const noexcept; class Guard; }; -inline constexpr Number::Number( - internalrep mantissa, - int exponent, - unchecked) noexcept +inline constexpr Number::Number(internalrep mantissa, int exponent, unchecked) noexcept : mantissa_{mantissa}, exponent_{exponent} { } @@ -338,15 +349,34 @@ 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_; + if (m > maxRep) + { + XRPL_ASSERT_PARTS( + m % 10 == 0, + "ripple::Number::mantissa", + "large mantissa has no remainder"); + m /= 10; + } + return static_cast(m); } -inline constexpr int +inline +constexpr int Number::exponent() const noexcept { + if (mantissa_ > maxRep) + { + XRPL_ASSERT_PARTS( + mantissa_ % 10 == 0, + "ripple::Number::exponent", + "large mantissa has no remainder"); + return exponent_ + 1; + } return exponent_; } @@ -432,25 +462,25 @@ 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_; diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 954d1ed01c..7e0568569e 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -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); +static_assert(std::is_same_v); namespace std { template <> -struct make_unsigned +struct make_unsigned { using type = uint128_t; }; @@ -256,13 +256,36 @@ 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 end up a 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. + // 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 +299,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_; @@ -306,16 +341,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) { @@ -451,16 +486,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) { @@ -521,16 +556,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; @@ -649,12 +684,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 +841,7 @@ root(Number f, unsigned d) return di - k2; }(); e += ex; - f = Number{f.mantissa(), f.exponent() - e}; // f /= 10^e; + f = Number{f.mantissa_, f.exponent_ - e}; // f /= 10^e; bool neg = false; if (f < Number{}) { @@ -838,7 +873,7 @@ 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}; + return Number{r.mantissa_, r.exponent_ + e / di}; } Number @@ -857,7 +892,7 @@ 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 = Number{f.mantissa_, f.exponent_ - e}; // f /= 10^e; // Quadratic least squares curve fit of f^(1/d) in the range [0, 1] auto const D = 105; @@ -878,7 +913,7 @@ root2(Number f) } while (r != rm1 && r != rm2); // return r * 10^(e/2) to reverse scaling - return Number{r.mantissa(), r.exponent() + e / 2}; + return Number{r.mantissa_, r.exponent_ + e / 2}; } // Returns f^(n/d) diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index 3419d5ba4b..1910b81409 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -202,7 +202,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::max() > + std::numeric_limits::max() > std::numeric_limits::max()); } else @@ -210,7 +210,7 @@ numberFromJson(SField const& field, Json::Value const& value) Throw("not a number"); } - numberint128 mantissa = parts.mantissa; + numberint mantissa = parts.mantissa; if (parts.negative) mantissa = -mantissa; diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 64037db395..ed5d640f59 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -120,12 +120,12 @@ public: test( Number{ - numberuint128(9'999'999'999'999'999) * 1000 + 999, + numberuint(9'999'999'999'999'999) * 1000 + 999, -3}, "9999999999999999"); test( -(Number{ - numberuint128(9'999'999'999'999'999) * 1000 + 999, + numberuint(9'999'999'999'999'999) * 1000 + 999, -3}), "-9999999999999999"); } @@ -149,14 +149,14 @@ public: test( Number{ - numberuint128(9'999'999'999'999'999) * 1000 + 999, + numberuint(9'999'999'999'999'999) * 1000 + 999, 0}, - "9999999999999999999"); + "9999999999999999990"); test( -(Number{ - numberuint128(9'999'999'999'999'999) * 1000 + 999, + numberuint(9'999'999'999'999'999) * 1000 + 999, 0}), - "-9999999999999999999"); + "-9999999999999999990"); } break; break; @@ -206,10 +206,10 @@ 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{-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{}, Number{5}, Number{5}}, {Number{5}, Number{}, Number{5}}, {Number{5'555'555'555'555'555'000, -32768}, @@ -228,17 +228,17 @@ 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{-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{}, 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{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}}}); auto test = [this](auto const& c) { for (auto const& [x, y, z] : c) { @@ -260,7 +260,7 @@ public: 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{numberint(9'999'999'999'999'999) * 1'000, 32768} + Number{5'000'000'000'000'000'000, 32767}; } catch (std::overflow_error const&) @@ -275,7 +275,7 @@ public: try { Number{ - numberuint128(9'999'999'999'999'999) * 1000 + 999, 32768} + + numberuint(9'999'999'999'999'999) * 1000 + 999, 32768} + Number{5'000'000'000'000'000'000, 32767}; } catch (std::overflow_error const&) @@ -315,11 +315,11 @@ 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{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{1'000'000'000'000'000, -15}, Number{1'000'000'000'000'000, -15}, Number{0}}, @@ -332,11 +332,11 @@ 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{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{1'000'000'000'000'000'000, -18}, Number{1'000'000'000'000'000'000, -18}, Number{0}}, @@ -423,7 +423,7 @@ 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{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, @@ -443,9 +443,9 @@ public: 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}}, + {Number{numberint(9'999'999'999'999'999) * 1000 + 999, 0}, + Number{numberint(9'999'999'999'999'999) * 1000 + 999, 0}, + Number{numberint(9'999'999'999'999'999) * 1000 + 998, 19}}, }); tests(cSmall, cLarge); } @@ -486,7 +486,7 @@ public: Number{1999999999999999861, -18}}, {Number{3214285714285706, -15}, Number{3111111111111119, -15}, - Number{numberint128(9999999999999999) * 1000 + 579, -18}}, + Number{numberint(9999999999999999) * 1000 + 579, -18}}, {Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, @@ -545,7 +545,7 @@ public: {Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{ - numberint128(9'999'999'999'999'999) * 1000 + 579, -18}}, + numberint(9'999'999'999'999'999) * 1000 + 579, -18}}, {Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}}, @@ -634,7 +634,7 @@ public: 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{numberint(9'999'999'999'999'999) * 1'000, 32768} * Number{5'000'000'000'000'000'000, 32767}; } catch (std::overflow_error const&) @@ -649,7 +649,7 @@ public: try { Number{ - numberint128(9'999'999'999'999'999) * 1000 + 999, 32768} + + numberint(9'999'999'999'999'999) * 1000 + 999, 32768} + Number{5'000'000'000'000'000'000, 32767}; } catch (std::overflow_error const&) @@ -722,10 +722,10 @@ public: {Number{1414213562373095049, -13}, Number{1414213562373095049, -13}, Number{1}}, - {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + {Number{numberint(9'999'999'999'999'999) * 1'000 + 999, 0}, Number{1'000'000'000'000'000'000}, Number{ - numberint128(9'999'999'999'999'999) * 1'000 + 999, + numberint(9'999'999'999'999'999) * 1'000 + 999, -18}}}); tests(cSmall, cLarge); } @@ -771,10 +771,10 @@ public: {Number{1414213562373095049, -13}, Number{1414213562373095049, -13}, Number{1}}, - {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + {Number{numberint(9'999'999'999'999'999) * 1'000 + 999, 0}, Number{1'000'000'000'000'000'000}, Number{ - numberint128(9'999'999'999'999'999) * 1'000 + 999, + numberint(9'999'999'999'999'999) * 1'000 + 999, -18}}}); tests(cSmall, cLarge); } @@ -820,10 +820,10 @@ public: {Number{1414213562373095049, -13}, Number{1414213562373095049, -13}, Number{1}}, - {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + {Number{numberint(9'999'999'999'999'999) * 1'000 + 999, 0}, Number{1'000'000'000'000'000'000}, Number{ - numberint128(9'999'999'999'999'999) * 1'000 + 999, + numberint(9'999'999'999'999'999) * 1'000 + 999, -18}}}); tests(cSmall, cLarge); } @@ -869,10 +869,10 @@ public: {Number{1414213562373095049, -13}, Number{1414213562373095049, -13}, Number{1}}, - {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + {Number{numberint(9'999'999'999'999'999) * 1'000 + 999, 0}, Number{1'000'000'000'000'000'000}, Number{ - numberint128(9'999'999'999'999'999) * 1'000 + 999, + numberint(9'999'999'999'999'999) * 1'000 + 999, -18}}}); tests(cSmall, cLarge); } @@ -1002,10 +1002,10 @@ public: {Number{-64}, 3, Number{-262144}}, {Number{64}, 11, - Number{numberint128(73786976294838206) * 1000 + 464, 0}}, + Number{numberint(73786976294838206) * 1000 + 464, 0}}, {Number{-64}, 11, - Number{-(numberint128(73786976294838206) * 1000 + 464), 0}}}; + Number{-(numberint(73786976294838206) * 1000 + 464), 0}}}; for (auto const& [x, y, z] : c) BEAST_EXPECT((power(x, y) == z)); } @@ -1393,7 +1393,7 @@ public: // 99999999999999999980000000000000000001 - also 38 digits BEAST_EXPECT( (power(max, 2) == - Number{numberint128(9'999'999'999'999'999) * 1000 + 998, 19})); + Number{numberint(9'999'999'999'999'999) * 1000 + 998, 19})); } } diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp index f860d0a5ca..eb9e53f8df 100644 --- a/src/test/protocol/STNumber_test.cpp +++ b/src/test/protocol/STNumber_test.cpp @@ -153,7 +153,7 @@ struct STNumber_test : public beast::unit_test::suite STNumber( sfNumber, -Number{ - numberint128(9'223'372'036'854'775) * 1000 + + numberint(9'223'372'036'854'775) * 1000 + 808, 0})); }