#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #pragma message("Using boost::multiprecision::uint128_t and int128_t") #include using uint128_t = boost::multiprecision::uint128_t; using int128_t = boost::multiprecision::int128_t; #else // !defined(_MSC_VER) using uint128_t = __uint128_t; using int128_t = __int128_t; #endif // !defined(_MSC_VER) namespace xrpl { thread_local Number::RoundingMode Number::mode = Number::RoundingMode::ToNearest; thread_local std::reference_wrapper Number::kRange = MantissaRange::Access::mantissaRange(MantissaRange::MantissaScale::Large330); std::string to_string(MantissaRange::MantissaScale const& scale) { switch (scale) { case MantissaRange::MantissaScale::Small: return "Small"; case MantissaRange::MantissaScale::LargeLegacy: return "LargeLegacy"; case MantissaRange::MantissaScale::Large320: return "Large320"; case MantissaRange::MantissaScale::Large330: return "Large330"; default: throw std::runtime_error("Bad scale"); // LCOV_EXCL_LINE } } std::string to_string(Number::RoundingMode const& round) { switch (round) { case Number::RoundingMode::ToNearest: return "ToNearest"; case Number::RoundingMode::TowardsZero: return "TowardsZero"; case Number::RoundingMode::Downward: return "Downward"; case Number::RoundingMode::Upward: return "Upward"; default: throw std::runtime_error("Bad rounding mode"); // LCOV_EXCL_LINE } } constexpr MantissaRange const& MantissaRange::Access::mantissaRange(MantissaScale scale) { static constexpr MantissaRange kSmall{MantissaScale::Small}; static constexpr MantissaRange kLegacy{MantissaScale::LargeLegacy}; static constexpr MantissaRange kLarge320{MantissaScale::Large320}; static constexpr MantissaRange kLarge330{MantissaScale::Large330}; switch (scale) { case MantissaScale::Small: return kSmall; case MantissaScale::LargeLegacy: return kLegacy; case MantissaScale::Large320: return kLarge320; case MantissaScale::Large330: return kLarge330; } throw std::logic_error("Unknown mantissa scale"); // static_asserts are checked at compile time, so it doesn't matter where in the function they // are located. For readability of the main body, put them after it. // Small static_assert(isPowerOfTen(kSmall.min)); static_assert(kSmall.min == 1'000'000'000'000'000LL); static_assert(kSmall.max == 9'999'999'999'999'999LL); static_assert(kSmall.log == 15); static_assert(kSmall.min < Number::kMaxRep); static_assert(kSmall.max < Number::kMaxRep); static_assert(kSmall.cuspRoundingFix == CuspRoundingFix::Disabled); // LargeLegacy static_assert(isPowerOfTen(kLegacy.min)); static_assert(kLegacy.min == 1'000'000'000'000'000'000ULL); static_assert(kLegacy.max == rep(9'999'999'999'999'999'999ULL)); static_assert(kLegacy.log == 18); static_assert(kLegacy.min < Number::kMaxRep); static_assert(kLegacy.max > Number::kMaxRep); static_assert(kLegacy.cuspRoundingFix == CuspRoundingFix::Disabled); // Large320 static_assert(isPowerOfTen(kLarge320.min)); static_assert(kLarge320.min == 1'000'000'000'000'000'000ULL); static_assert(kLarge320.max == rep(9'999'999'999'999'999'999ULL)); static_assert(kLarge320.log == 18); static_assert(kLarge320.min < Number::kMaxRep); static_assert(kLarge320.max > Number::kMaxRep); static_assert(kLarge320.cuspRoundingFix == CuspRoundingFix::Enabled320); // Large330 static_assert(isPowerOfTen(kLarge330.min)); static_assert(kLarge330.min == 1'000'000'000'000'000'000ULL); static_assert(kLarge330.max == rep(9'999'999'999'999'999'999ULL)); static_assert(kLarge330.log == 18); static_assert(kLarge330.min < Number::kMaxRep); static_assert(kLarge330.max > Number::kMaxRep); static_assert(kLarge330.cuspRoundingFix == CuspRoundingFix::Enabled330); } Number::RoundingMode Number::getround() { return mode; } Number::RoundingMode Number::setround(RoundingMode inMode) { return std::exchange(Number::mode, inMode); } MantissaRange::MantissaScale Number::getMantissaScale() { return kRange.get().scale; } void Number::setMantissaScale(MantissaRange::MantissaScale scale) { if (!MantissaRange::getAllScales().contains(scale)) logicError("Unknown mantissa scale"); kRange = MantissaRange::Access::mantissaRange(scale); } // Optimization equivalent to: // auto r = static_cast(u % 10); // u /= 10; // return r; // Derived from Hacker's Delight Second Edition Chapter 10 // by Henry S. Warren, Jr. static inline unsigned divu10(uint128_t& u) { // q = u * 0.75 auto q = (u >> 1) + (u >> 2); // iterate towards q = u * 0.8 q += q >> 4; q += q >> 8; q += q >> 16; q += q >> 32; q += q >> 64; // q /= 8 approximately == u / 10 q >>= 3; // r = u - q * 10 approximately == u % 10 auto r = static_cast(u - ((q << 3) + (q << 1))); // correction c is 1 if r >= 10 else 0 auto c = (r + 6) >> 4; u = q + c; r -= c * 10; return r; } template concept UnsignedMantissa = std::is_unsigned_v || std::is_same_v; /** * Guard * * The Guard class is used to temporarily add extra digits of * precision to an operation. This enables the final result * to be correctly rounded to the internal precision of Number. * * At its core, the Guard really only needs three pieces of information to determine how to round: * 1. The rounding mode * 2. The last digit dropped from the mantissa (i.e. the first digit after the decimal point). * (first byte of digits_) * 3. Whether any other non-zero digits were dropped from the mantissa. (remaining bytes of digits_ * and xbit_) * * Upward and Downward rounding modes round the unsigned mantissa toward or away from zero * depending on whether the sign is negative (sbit_). For positive values, Upward is away, and * Downward is toward. For negative values, that's reversed. For simplicity, I'm going to describe * the logic using "TowardZero" and "AwayFromZero". * * TowardZero is the easiest rounding mode. It always rounds down. digits_ and xbit_ are * irrelevant. * AwayFromZero is almost as simple. If both "digits_" and "xbit_" are zero (0), it rounds down. * Else it rounds up. * ToNearest is only a little more complicated. If the last dropped digit is < 5, then round * down. If it is > 5, round up. If it is exactly 5, and there are _any_ other digits (the * remainder of "digits_" or "xbit_"), round up, else round to even. * * The current implementation stores 16 digits in "digits_" so that digits can be "pop"ped back * out if needed during subtraction (negative addition) operations. */ class Number::Guard { std::uint64_t digits_{0}; // 16 decimal guard digits std::uint8_t xbit_ : 1 {0}; // has a non-zero digit been shifted off the end std::uint8_t sbit_ : 1 {0}; // the sign of the guard digits public: internalrep const minMantissa; internalrep const maxMantissa; MantissaRange::CuspRoundingFix const cuspRoundingFix; explicit Guard( internalrep const& minMantissa, internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFix) : minMantissa(minMantissa), maxMantissa(maxMantissa), cuspRoundingFix(cuspRoundingFix) { } explicit Guard(MantissaRange const& range) : Guard(range.min, range.max, range.cuspRoundingFix) { } // set & test the sign bit void setPositive() noexcept; void setNegative() noexcept; // Should only be called by doNormalize, and then only for division // operations with remainders. void setDropped() noexcept; [[nodiscard]] bool isNegative() const noexcept; // add a digit template void push(T d) noexcept; // recover a digit unsigned pop() noexcept; // if true, there are no digits in the guard, including dropped digits (xbit_) [[nodiscard]] bool empty() const noexcept; /** * Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in * this Guard. * * Substitute for: * push(mantissa % 10); * mantissa /= 10; * ++exponent; */ template void doDropDigit(T& mantissa, int& exponent) noexcept; enum class Round { // The result is exact. No rounding is needed. Only used if cuspRoundingFix is Enabled330 or // higher. Exact = -2, // Round down. Since we use integer math, that usually means no change is needed. // Exceptions are for when the result is between kMaxRep and kMaxRepUp (round to kMaxRep), // or after subtraction where _any_ remainder will modify the result. The latter is what // distinguishes Exact from Down. Down = -1, // The result was exactly half-way between two integers. This will round to even. Even = 0, // Round up. Always adds 1 (or subtracts 1 in some cases if cuspRoundingFix is not // Enabled) Up = 1, }; // Indicate round direction: 1 is up, -1 is down, 0 is even // This enables the client to round towards nearest, and on // tie, round towards even. [[nodiscard]] Round round() const noexcept; // Modify the result to the correctly rounded value template void doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location); // Modify the result to the correctly rounded value template void doRoundDown(bool& negative, T& mantissa, int& exponent); // Modify the result to the correctly rounded value void doRound(rep& drops, std::string location) const; private: void doPush(unsigned d) noexcept; template void bringIntoRange(bool& negative, T& mantissa, int& exponent); }; inline void Number::Guard::setPositive() noexcept { sbit_ = 0; } inline void Number::Guard::setNegative() noexcept { sbit_ = 1; } inline void Number::Guard::setDropped() noexcept { xbit_ = 1; } inline bool Number::Guard::isNegative() const noexcept { return sbit_ == 1; } inline void Number::Guard::doPush(unsigned d) noexcept { xbit_ = xbit_ || ((digits_ & 0x0000'0000'0000'000F) != 0); digits_ >>= 4; digits_ |= (d & 0x0000'0000'0000'000FULL) << 60; } template inline void Number::Guard::push(T d) noexcept { doPush(static_cast(d)); } inline unsigned Number::Guard::pop() noexcept { unsigned const d = (digits_ & 0xF000'0000'0000'0000) >> 60; digits_ <<= 4; return d; } inline bool Number::Guard::empty() const noexcept { return digits_ == 0 && !xbit_; } template void Number::Guard::doDropDigit(T& mantissa, int& exponent) noexcept { push(mantissa % 10); mantissa /= 10; ++exponent; } // Use the divu10 optimization for uint128s template <> void Number::Guard::doDropDigit(uint128_t& mantissa, int& exponent) noexcept { // The following is optimization for: // push(static_cast(mantissa % 10)); // mantissa /= 10; push(divu10(mantissa)); ++exponent; } // Returns: // -1 if Guard is less than half // 0 if Guard is exactly half // 1 if Guard is greater than half Number::Guard::Round Number::Guard::round() const noexcept { // Local "mode" shadows and has the same value as the static thread_local "Number::mode". // This ensures the overhead of loading the thread_local is only incurred once. auto const mode = Number::getround(); if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330 && empty()) { // No remainder return Round::Exact; } if (mode == RoundingMode::TowardsZero) return Round::Down; // Also Towards Zero if ((mode == RoundingMode::Downward && !sbit_) || (mode == RoundingMode::Upward && sbit_)) { return Round::Down; } // Away from Zero. Since we checked sbit_ in the previous block, we don't need to check it // again. if (mode == RoundingMode::Downward || mode == RoundingMode::Upward) { if (empty()) return Round::Down; return Round::Up; } XRPL_ASSERT( mode == RoundingMode::ToNearest, "xrpl::Number::Guard::Round : fallthrough to ToNearest"); // assume round to nearest if mode is not one of the predefined values if (digits_ > 0x5000'0000'0000'0000) return Round::Up; if (digits_ < 0x5000'0000'0000'0000) return Round::Down; if (xbit_) return Round::Up; return Round::Even; } template void Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent) { // Bring mantissa back into the minMantissa / maxMantissa range AFTER // rounding if (mantissa < minMantissa) { mantissa *= 10; --exponent; } if (exponent < kMinExponent) { static constexpr Number kZero = Number{}; negative = kZero.negative_; mantissa = kZero.mantissa_; exponent = kZero.exponent_; } } template void Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location) { auto r = round(); if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1)) { auto const safeToIncrement = [this](auto const& mantissa) { return mantissa < maxMantissa && mantissa < kMaxRep; }; if (cuspRoundingFix != MantissaRange::CuspRoundingFix::Disabled) { // Ensure mantissa after incrementing fits within both the // min/maxMantissa range and is a valid "rep". if (safeToIncrement(mantissa)) { // Nothing unusual here, just increment the mantissa ++mantissa; } else { // Incrementing the mantissa will require dividing, which will require rounding. So // _don't_ increment the mantissa. Instead, divide and round recursively. It should // be impossible to recurse more than once, because once the mantissa is divided by // 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1 will have no // chance of bringing it back over. doDropDigit(mantissa, exponent); XRPL_ASSERT_PARTS( safeToIncrement(mantissa), "xrpl::Number::Guard::doRoundUp", "can't recurse more than once"); doRoundUp(negative, mantissa, exponent, location); return; } } else { // Need to preserve the incorrect behavior until the fix amendment can be retired, // because otherwise would risk an unplanned ledger fork. ++mantissa; // Ensure mantissa after incrementing fits within both the // min/maxMantissa range and is a valid "rep". if (mantissa > maxMantissa || mantissa > kMaxRep) { // Don't use doDropDigit here mantissa /= 10; ++exponent; } } } bringIntoRange(negative, mantissa, exponent); if (exponent > kMaxExponent) Throw(std::string(location)); } template void Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent) { auto r = round(); if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330) { // If there was any remainder, subtract 1 from the result. This is sufficient to get the // best rounding. XRPL_ASSERT( r == Round::Exact || mantissa > maxMantissa, "xrpl::Number::Guard::doRoundDown : mantissa is expected size"); if (r != Round::Exact) { --mantissa; } } else { // Need to preserve the incorrect behavior until the fix amendment can be retired, // because otherwise would risk an unplanned ledger fork. if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1)) { --mantissa; if (mantissa < minMantissa) { mantissa *= 10; --exponent; } } } bringIntoRange(negative, mantissa, exponent); } // Modify the result to the correctly rounded value void Number::Guard::doRound(rep& drops, std::string location) const { auto r = round(); if (r == Round::Up || (r == Round::Even && (drops & 1) == 1)) { if (drops >= kMaxRep) { static_assert(sizeof(internalrep) == sizeof(rep)); // This should be impossible, because it's impossible to represent // "kMaxRep + 0.6" in Number, regardless of the scale. There aren't // enough digits available. You'd either get a mantissa of "kMaxRep" // or "(kMaxRep + 1) / 10", neither of which will round up when // converting to rep, though the latter might overflow _before_ // rounding. Throw(std::string(location)); // LCOV_EXCL_LINE } ++drops; } if (isNegative()) drops = -drops; } // Number // Safely convert rep (int64) mantissa to internalrep (uint64). If the rep is // negative, returns the positive value. This takes a little extra work because // converting std::numeric_limits::min() flirts with UB, and can // vary across compilers. Number::internalrep Number::externalToInternal(rep mantissa) { // If the mantissa is already positive, just return it if (mantissa >= 0) return mantissa; // If the mantissa is negative, but fits within the positive range of rep, // return it negated if (mantissa >= -std::numeric_limits::max()) return -mantissa; // If the mantissa doesn't fit within the positive range, convert to // int128_t, negate that, and cast it back down to the internalrep // In practice, this is only going to cover the case of // std::numeric_limits::min(). int128_t const temp = mantissa; return static_cast(-temp); } Number Number::one() { auto const& range = kRange.get(); return Number{false, range.min, -range.log, Number::Unchecked{}}; } template void doNormalize( bool& negative, T& mantissa, int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFix, bool dropped) { static constexpr auto kMinExponent = Number::kMinExponent; static constexpr auto kMaxExponent = Number::kMaxExponent; static constexpr auto kMaxRep = Number::kMaxRep; using Guard = Number::Guard; static constexpr Number kZero = Number{}; if (mantissa == 0) { mantissa = kZero.mantissa_; exponent = kZero.exponent_; negative = kZero.negative_; return; } auto m = mantissa; while ((m < minMantissa) && (exponent > kMinExponent)) { m *= 10; --exponent; } Guard g(minMantissa, maxMantissa, cuspRoundingFix); if (negative) g.setNegative(); if (dropped) g.setDropped(); while (m > maxMantissa) { if (exponent >= kMaxExponent) throw std::overflow_error("Number::normalize 1"); g.doDropDigit(m, exponent); } if ((exponent < kMinExponent) || (m < minMantissa)) { mantissa = kZero.mantissa_; exponent = kZero.exponent_; negative = kZero.negative_; 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 // MantissaRange. Cut it down here so that the rounding will be done while // it's smaller. // // Example: 9,900,000,000,000,123,456 > 9,223,372,036,854,775,807, // so "m" will be modified to 990,000,000,000,012,345. Then that value // will be rounded to 990,000,000,000,012,345 or // 990,000,000,000,012,346, 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,123,450 or 9,900,000,000,000,123,460. // mantissa() will return mantissa / 10, and exponent() will return // exponent + 1. if (m > kMaxRep) { if (exponent >= kMaxExponent) throw std::overflow_error("Number::normalize 1.5"); g.doDropDigit(m, exponent); } // Before modification, m should be within the min/max range. After // modification, it must be less than kMaxRep. In other words, the original // value should have been no more than kMaxRep * 10. // (kMaxRep * 10 > maxMantissa) XRPL_ASSERT_PARTS(m <= kMaxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64"); mantissa = m; g.doRoundUp(negative, mantissa, exponent, "Number::normalize 2"); XRPL_ASSERT_PARTS( mantissa >= minMantissa && mantissa <= maxMantissa, "xrpl::doNormalize", "final mantissa fits in range"); } template <> void Number::normalize( bool& negative, uint128_t& mantissa, int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFix) { // Not used by every compiler version, and thus not necessarily // counted by coverage build // LCOV_EXCL_START doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false); // LCOV_EXCL_STOP } template <> void Number::normalize( bool& negative, unsigned long long& mantissa, int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFix) { // Not used by every compiler version, and thus not necessarily // counted by coverage build // LCOV_EXCL_START doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false); // LCOV_EXCL_STOP } template <> void Number::normalize( bool& negative, unsigned long& mantissa, int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFix) { doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false); } void Number::normalize(MantissaRange const& range) { normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFix); } void Number::normalize(Guard const& guard) { normalize( negative_, mantissa_, exponent_, guard.minMantissa, guard.maxMantissa, guard.cuspRoundingFix); } // 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(), "xrpl::Number::shiftExponent", "normalized"); auto const newExponent = exponent_ + exponentDelta; if (newExponent >= kMaxExponent) throw std::overflow_error("Number::shiftExponent"); if (newExponent < kMinExponent) { return Number{}; } Number const result{negative_, mantissa_, newExponent, Unchecked{}}; XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::Number::shiftExponent", "result is normalized"); return result; } Number& Number::operator+=(Number const& y) { static constexpr Number kZero = Number{}; if (y == kZero) return *this; if (*this == kZero) { *this = y; return *this; } if (*this == -y) { *this = kZero; return *this; } XRPL_ASSERT(isnormal() && y.isnormal(), "xrpl::Number::operator+=(Number) : is normal"); // *n = negative // *s = sign // *m = mantissa // *e = exponent // Need to use uint128_t, because large mantissas can overflow when added // together. bool xn = negative_; uint128_t xm = mantissa_; auto xe = exponent_; bool const yn = y.negative_; uint128_t ym = y.mantissa_; auto ye = y.exponent_; Guard g(kRange); auto const& minMantissa = g.minMantissa; auto const& maxMantissa = g.maxMantissa; auto const cuspRoundingFix = g.cuspRoundingFix; // Bring the exponents of both values into agreement, so the mantissas are on the same scale // and can be added directly together. auto const upperLimit = static_cast(g.minMantissa) * 1000; // For the "adjust" lambda // expandM / expandE: The values for which the mantissa will be expanded, and the exponent // decreased to match. Mantissa won't be expanded beyond upperLimit. // (37e8 == 37000e5 == 37000000e2) // shrinkM / shrinkE: The values for which the mantissa will be shrunk, and exponent increased // to match, if necessary. auto const adjust = [&g, &upperLimit]( uint128_t& expandM, int& expandE, uint128_t& shrinkM, int& shrinkE) { // Adjust up and down until the exponents match if (g.cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330) { // For Enabled330, there are three steps. // 1. First, shrink the mantissa of shrinkM/shrinkE while shrinkM ends in 0. while (shrinkE < expandE && shrinkM % 10 == 0) { g.doDropDigit(shrinkM, shrinkE); } // 2. Then expand the mantissa of expandM/expandE, with a limit for expandM a few orders // of magnitude above the MantissaRange. This will leave a few extra digits for rounding // later, but nothing excessive. while (shrinkE < expandE && expandE > kMinExponent && expandM < upperLimit) { expandM *= 10; --expandE; } } // 3. Finally, shrink the mantissa of shrinkM/shrinkE until the exponents match. Any removed // digits will be put into the Guard. This is the only step for non-Enabled330 modes. while (shrinkE < expandE) { g.doDropDigit(shrinkM, shrinkE); } }; // Shrink the mantissa and raise the exponent of the value with the lower exponent. Store any // dropped digits in the Guard. if (xe < ye) { if (xn) g.setNegative(); adjust(ym, ye, xm, xe); } else if (xe > ye) { if (yn) g.setNegative(); adjust(xm, xe, ym, ye); } else if (g.cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330) { // Both values have the same exponent. // Set the sign of the Guard based on the sign of the Number with the smallest // unsigned _mantissa_ if ((xm < ym && xn) || (ym < xm && yn)) g.setNegative(); } if (xn == yn) { xm += ym; if (g.cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330) { // Don't do any adjustments for Enabled330. Normalize will take care of it // Because of "adjust", the only way there can be data in the Guard is if we first grew // the mantissa past the maxMantissa. Since we added here, it can only get bigger. // If xm > maxMantissa, then doNormalize has all the data it needs from the last 3-4 // digits, plus the "dropped" flag that will be passed in. // If not, then the mantissa will only need to be padded out with 0s and won't need to // round. XRPL_ASSERT( xm > maxMantissa || g.empty(), "xrpl::Number::operator+ : rounding state expected after add"); } else { if (xm > maxMantissa || xm > kMaxRep) { g.doDropDigit(xm, xe); } g.doRoundUp(xn, xm, xe, "Number::addition overflow"); } } else { if (xm > ym) { xm = xm - ym; } else { xm = ym - xm; xe = ye; xn = yn; } if (cuspRoundingFix >= MantissaRange::CuspRoundingFix::Enabled330) { // Because we subtracted, xm can have any number of digits from 1 up to // upperLimit * 10, and g can be in any state. (Note that xm can't be zero, because that // special case was tested earlier.) // Grow xm/xe and pull digits out of the Guard until xm reaches upperLimit, but stop if // the Guard empties out, because no rounding will be necessary. This will ensure that // normalize will have enough information to make an accurate rounding decision. // (Normalize will pad a small mantissa back into range.) Note that if any digits were // lost (xbit_), the Guard will never be empty, so xm will grow larger than upperLimit. while (xm < upperLimit && !g.empty()) { xm *= 10; xm -= g.pop(); --xe; } XRPL_ASSERT( xm > maxMantissa || g.empty(), "xrpl::Number::operator+ : rounding state expected after subtract"); } else { // Grow xm/xe and pull digits out of the Guard until it's back in the // minMantissa/maxMantissa range. while (xm < minMantissa && xm * 10 <= kMaxRep) { xm *= 10; xm -= g.pop(); --xe; } } // Rounding down can result in decrementing xm, based on whether there is any data left in // the Guard (depending on cuspRoundingFix). Note that if that happens, then the Guard is // not empty. For Enabled330, that will also result in the "dropped" flag being passed to // doNormalize, which may result in the mantissa being incremented again. It doesn't matter // what the dropped digits are, only that they exist. This is because subtracting one // "overcorrects", so we know there are still trailing digits to be accounted for in the // rounding. // // This works because // 1. The rounding up will be done _after_ the mantissa is brought into range. It may not // be in range right now, and // 2. The "dropped" flag is only ever used as a tie-breaker, specifically when rounding // away from zero, and the dropped digits are 0, or when rounding to nearest, and // the dropped digits represent exactly 0.5. g.doRoundDown(xn, xm, xe); } doNormalize( xn, xm, xe, minMantissa, maxMantissa, cuspRoundingFix, cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330 && !g.empty()); negative_ = xn; mantissa_ = static_cast(xm); exponent_ = xe; XRPL_ASSERT(isnormal(), "xrpl::Number::operator+= : result is normal"); return *this; } Number& Number::operator*=(Number const& y) { static constexpr Number kZero = Number{}; if (*this == kZero) return *this; if (y == kZero) { *this = y; return *this; } // *n = negative // *s = sign // *m = mantissa // *e = exponent bool const xn = negative_; int const xs = xn ? -1 : 1; internalrep xm = mantissa_; auto xe = exponent_; bool const yn = y.negative_; int const ys = yn ? -1 : 1; internalrep const ym = y.mantissa_; auto ye = y.exponent_; auto zm = uint128_t(xm) * uint128_t(ym); auto ze = xe + ye; auto zs = xs * ys; bool zn = (zs == -1); Guard g(kRange); if (zn) g.setNegative(); auto const& maxMantissa = g.maxMantissa; while (zm > maxMantissa || zm > kMaxRep) { g.doDropDigit(zm, ze); } xm = static_cast(zm); xe = ze; g.doRoundUp(zn, xm, xe, "Number::multiplication overflow : exponent is " + std::to_string(xe)); negative_ = zn; mantissa_ = xm; exponent_ = xe; normalize(g); return *this; } Number& Number::operator/=(Number const& y) { static constexpr Number kZero = Number{}; if (y == kZero) throw std::overflow_error("Number: divide by 0"); if (*this == kZero) return *this; // n* = numerator // d* = denominator // z* = result (quotient) // *p = negative (p for positive, even though the value means not // positive?) // *s = sign // *m = mantissa // *e = exponent bool const np = negative_; int const ns = (np ? -1 : 1); auto nm = mantissa_; auto ne = exponent_; bool const dp = y.negative_; int const ds = (dp ? -1 : 1); // Create the denominator as 128-bit unsigned, since that's what we // need to work with. auto const dm = static_cast(y.mantissa_); auto const de = y.exponent_; auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; auto const cuspRoundingFix = range.cuspRoundingFix; // Division operates on two large integers (16-digit for small // mantissas, 19-digit for large) using integer math. If the values // were just divided directly, the result would be only ever be one // digit or zero - not very useful. // e.g. 9'876'543'210'987'654 / 1'234'567'890'123'456 = 8 // 1'234'567'890'123'456 / 9'876'543'210'987'654 = 0 // Introduce a power-of-ten multiplication factor for the numerator // which will ensure the result has a meaningful number of digits. // // Consider numbers with a 2-digit mantissa: // * Assume both numbers have an exponent of 0, using "ToNearest" rounding // * 23 / 67 = 0 // * Use a factor of 10^4 // * 230'000 / 67 = 3432 with an exponent of -4 // * The normalized result will be 34, exponent -2, or 0.34 // // The most extreme results are 10/99 and 99/10 // * 100'000 / 99 = 1'010e-4 = 10e-2 or 0.10 // * 990'000 / 10 = 99'000e-4 = 99e-1 or 9.9 // // Note that the computations give 2 or 3 digits after the // decimal point to determine which way to round for most scenarios. // // For small mantissas (where the MantissaRange.log == 15), shifting by 10^17 gives sufficient // precision while not overflowing uint128_t or the cast back to int64_t. (This is legacy // behavior, which must not be changed.) // // For large mantissas (where the MantissaRange.log == 18), a shift by 10^20 would be optimal // for most scenarios. However, larger mantissa values would overflow 2^128. // // * log(2^128,10) ~ 38.5 // * largeRange.log = 18, fits in 10^19 // * The expanded numerator must fit in 10^38 // * f not be more than 10^(38-19) = 10^19 safely // // So, we do the division into stages: // // Stage 1: Use the same factor of 10^17, for the initial division. This // will frequently not result in a whole number quotient. // // Stage 2: If there is a remainder from the first step, repeat the // process with a "correction" factor of 10^5. Shift the // result of Stage 1 over by 5 places, and add the second result to it. // This is equivalent to if we had used an initial factor of 10^22, // a couple digits more than we actually need. // // Stage 3: If there is still a remainder, and the cuspRoundingFix // is enabled, pass a flag indicating such to doNormalize. The Guard // in doNormalize will treat that flag as if non-zero digits had // been dropped from the mantissa when shrinking it into range. // This is only relevant when rounding away from zero (Upward for // positive numbers, Downward for negative), or if the "regular" // remainder is exactly 0.5 for "ToNearest". This will give the // rounding the most accurate result possible, as if infinite // precision was used in the initial calculation. // Stage 1: Do the initial division with a factor of 10^17. auto constexpr factorExponent = 17; uint128_t constexpr f = kPowerOfTen[factorExponent]; auto const numerator = uint128_t(nm) * f; auto zm = numerator / dm; auto ze = ne - de - factorExponent; bool zp = (ns * ds) < 0; // dropped is used in the same way as Guard::xbit_. In the case of // division, it indicates if there's any remainder left over after // we have been as precise as reasonable. If there is, it would be as // if we were using infinite precision math, and a non-zero digit // had been shifted off the end of the result when normalizing. bool dropped = false; if (range.scale != MantissaRange::MantissaScale::Small) { // Stage 2 // // If there is a remainder, treat it as a secondary numerator. // Multiply by correctionFactor separately from stage 1. // The math for this would work for small mantissas, but we need to // preserve legacy behavior. // // Consider: // ((numerator * correctionFactor) / dm) / correctionFactor // = ((numerator / dm) * correctionFactor) / correctionFactor) // // But that assumes infinite precision. With integer math, this is // equivalent to // // = ((numerator / dm * correctionFactor) // + ((numerator % dm) * correctionFactor) / dm) / correctionFactor // = ((zm * correctionFactor) // + (remainder * correctionFactor) / dm) / correctionFactor // // The trick is that multiplication by correctionFactor is done on the mantissa, but // division by correctionFactor is done by modifying the exponent, so no precision is lost // until we normalize. // // If remainder is zero, we can skip this stage entirely because // the first stage gave an exact answer. auto constexpr correctionExponent = 5; uint128_t constexpr correctionFactor = kPowerOfTen[correctionExponent]; static_assert(factorExponent + correctionExponent == 22); auto const remainder = (numerator % dm); if (remainder != 0) { auto const partialNumerator = remainder * correctionFactor; auto const correction = partialNumerator / dm; // If the correction is zero, we do not have to make any // modifications to z*, because it will not have any // effect on the final result. (We'd be adding a bunch of // zeros to the end of zm that would just be removed in // normalize.) However, if that is the case, then Stage 3 is // even more important for accuracy. if (correction != 0) { zm *= correctionFactor; // divide by the correctionFactor by moving the exponent, so we don't lose the // integer value we just computed ze -= correctionExponent; zm += correction; } // Stage 3: If there's still anything left, and the cusp // rounding fix is enabled, flag if there is still // a remainder from stage 2. bool const useTrailingRemainder = cuspRoundingFix != MantissaRange::CuspRoundingFix::Disabled; if (useTrailingRemainder) { dropped = partialNumerator % dm != 0; } } } doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFix, dropped); negative_ = zp; mantissa_ = static_cast(zm); exponent_ = ze; XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::operator/=", "result is normalized"); return *this; } Number:: operator rep() const { rep drops = mantissa(); int offset = exponent(); Guard g(kRange); if (drops != 0) { if (negative_) { g.setNegative(); drops = -drops; } while (offset < 0) { g.doDropDigit(drops, offset); } for (; offset > 0; --offset) { if (drops > kMaxRep / 10) throw std::overflow_error("Number::operator rep() overflow"); drops *= 10; } g.doRound(drops, "Number::operator rep() rounding overflow"); } return drops; } Number Number::truncate() const noexcept { if (exponent_ >= 0 || mantissa_ == 0) return *this; Number ret = *this; while (ret.exponent_ < 0 && ret.mantissa_ != 0) { ret.exponent_ += 1; ret.mantissa_ /= rep(10); } // We are guaranteed that normalize() will never throw an exception // because exponent is either negative or zero at this point. ret.normalize(kRange); return ret; } std::string to_string(Number const& amount) { // keep full internal accuracy, but make more human friendly if possible static constexpr Number kZero = Number{}; if (amount == kZero) return "0"; auto exponent = amount.exponent_; auto 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))))) { while (mantissa != 0 && mantissa % 10 == 0 && exponent < Number::kMaxExponent) { mantissa /= 10; ++exponent; } std::string ret = negative ? "-" : ""; ret.append(std::to_string(mantissa)); ret.append(1, 'e'); ret.append(std::to_string(exponent)); return ret; } XRPL_ASSERT(exponent + 43 > 0, "xrpl::to_string(Number) : minimum exponent"); ptrdiff_t const padPrefix = rangeLog + 12; ptrdiff_t const padSuffix = rangeLog + 8; std::string const rawValue(std::to_string(mantissa)); std::string val; val.reserve(rawValue.length() + padPrefix + padSuffix); val.append(padPrefix, '0'); val.append(rawValue); val.append(padSuffix, '0'); ptrdiff_t const offset(exponent + padPrefix + rangeLog + 1); auto preFrom(val.begin()); auto const preTo(val.begin() + offset); auto const postFrom(val.begin() + offset); auto postTo(val.end()); // Crop leading zeroes. Take advantage of the fact that there's always a // fixed amount of leading zeroes and skip them. if (std::distance(preFrom, preTo) > padPrefix) preFrom += padPrefix; XRPL_ASSERT(postTo >= postFrom, "xrpl::to_string(Number) : first distance check"); preFrom = std::find_if(preFrom, preTo, [](char c) { return c != '0'; }); // Crop trailing zeroes. Take advantage of the fact that there's always a // fixed amount of trailing zeroes and skip them. if (std::distance(postFrom, postTo) > padSuffix) postTo -= padSuffix; XRPL_ASSERT(postTo >= postFrom, "xrpl::to_string(Number) : second distance check"); postTo = std::find_if( std::make_reverse_iterator(postTo), std::make_reverse_iterator(postFrom), [](char c) { return c != '0'; }) .base(); std::string ret; if (negative) ret.append(1, '-'); // Assemble the output: if (preFrom == preTo) { ret.append(1, '0'); } else { ret.append(preFrom, preTo); } if (postTo != postFrom) { ret.append(1, '.'); ret.append(postFrom, postTo); } return ret; } // Returns f^n // Uses a log_2(n) number of multiplications Number power(Number const& f, unsigned n) { if (n == 0) return Number::one(); if (n == 1) return f; auto r = power(f, n / 2); r *= r; if (n % 2 != 0) r *= f; return r; } // Returns f^(1/d) // Uses Newton–Raphson iterations until the result stops changing // to find the non-negative root of the polynomial g(x) = x^d - f // This function, and power(Number f, unsigned n, unsigned d) // treat corner cases such as 0 roots as advised by Annex F of // the C standard, which itself is consistent with the IEEE // floating point standards. Number root(Number f, unsigned d) { static constexpr Number kZero = Number{}; auto const one = Number::one(); if (f == one || d == 1) return f; if (d == 0) { if (f == -one) return one; if (abs(f) < one) return kZero; throw std::overflow_error("Number::root infinity"); } if (f < kZero && d % 2 == 0) throw std::overflow_error("Number::root nan"); if (f == kZero) return f; // Scale f into the range (0, 1) such that f's exponent is a multiple of d auto e = f.exponent_ + Number::mantissaLog() + 1; auto const di = static_cast(d); auto ex = [e = e, di = di]() // Euclidean remainder of e/d { int const k = (e >= 0 ? e : e - (di - 1)) / di; int const k2 = e - (k * di); if (k2 == 0) return 0; return di - k2; }(); e += ex; f = f.shiftExponent(-e); // f /= 10^e; XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root(Number, unsigned)", "f is normalized"); bool neg = false; if (f < kZero) { neg = true; f = -f; } // Quadratic least squares curve fit of f^(1/d) in the range [0, 1] // NOLINTNEXTLINE(readability-identifier-naming) auto const D = (((((6 * di) + 11) * di) + 6) * di) + 1; auto const a0 = 3 * di * ((((2 * di) - 3) * di) + 1); auto const a1 = 24 * di * ((2 * di) - 1); auto const a2 = -30 * (di - 1) * di; Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D}; if (neg) { f = -f; r = -r; } // Newton–Raphson iteration of f^(1/d) with initial guess r // halt when r stops changing, checking for bouncing on the last iteration Number rm1{}; Number rm2{}; do { rm2 = rm1; rm1 = r; r = (Number(d - 1) * r + f / power(r, d - 1)) / Number(d); } while (r != rm1 && r != rm2); // return r * 10^(e/d) to reverse scaling auto const result = r.shiftExponent(e / di); XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root(Number, unsigned)", "result is normalized"); return result; } Number root2(Number f) { static constexpr Number kZero = Number{}; auto const one = Number::one(); if (f == one) return f; if (f < kZero) throw std::overflow_error("Number::root nan"); if (f == kZero) return f; // Scale f into the range (0, 1) such that f's exponent is a multiple of d auto e = f.exponent_ + Number::mantissaLog() + 1; if (e % 2 != 0) ++e; f = f.shiftExponent(-e); // f /= 10^e; XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root2(Number)", "f is normalized"); // Quadratic least squares curve fit of f^(1/d) in the range [0, 1] auto const D = 105; // NOLINT(readability-identifier-naming) auto const a0 = 18; auto const a1 = 144; auto const a2 = -60; Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D}; // Newton–Raphson iteration of f^(1/2) with initial guess r // halt when r stops changing, checking for bouncing on the last iteration Number rm1{}; Number rm2{}; do { rm2 = rm1; rm1 = r; r = (r + f / r) / Number(2); } while (r != rm1 && r != rm2); // return r * 10^(e/2) to reverse scaling auto const result = r.shiftExponent(e / 2); XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root2(Number)", "result is normalized"); return result; } // Returns f^(n/d) Number power(Number const& f, unsigned n, unsigned d) { static constexpr Number kZero = Number{}; auto const one = Number::one(); if (f == one) return f; auto g = std::gcd(n, d); if (g == 0) throw std::overflow_error("Number::power nan"); if (d == 0) { if (f == -one) return one; if (abs(f) < one) return kZero; // abs(f) > one throw std::overflow_error("Number::power infinity"); } if (n == 0) return one; n /= g; d /= g; if ((n % 2) == 1 && (d % 2) == 0 && f < kZero) throw std::overflow_error("Number::power nan"); return root(power(f, n), d); } } // namespace xrpl