//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2022 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_BASICS_NUMBER_H_INCLUDED #define RIPPLE_BASICS_NUMBER_H_INCLUDED #include #include #include #include namespace ripple { class Number; std::string to_string(Number const& amount); class Number { using rep = std::int64_t; rep mantissa_{0}; int exponent_{std::numeric_limits::lowest()}; public: // The range for the mantissa when normalized constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL; constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL; // The range for the exponent when normalized constexpr static int minExponent = -32768; constexpr static int maxExponent = 32768; struct unchecked { explicit unchecked() = default; }; explicit constexpr Number() = default; Number(rep mantissa); explicit Number(rep mantissa, int exponent); explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept; constexpr rep mantissa() const noexcept; constexpr int exponent() const noexcept; constexpr Number operator+() const noexcept; constexpr Number operator-() const noexcept; Number& operator++(); Number operator++(int); Number& operator--(); Number operator--(int); Number& operator+=(Number const& x); Number& operator-=(Number const& x); Number& operator*=(Number const& x); Number& operator/=(Number const& x); static constexpr Number min() noexcept; static constexpr Number max() noexcept; static constexpr Number lowest() noexcept; /** Conversions to Number are implicit and conversions away from Number * are explicit. This design encourages and facilitates the use of Number * as the preferred type for floating point arithmetic as it makes * "mixed mode" more convenient, e.g. MPTAmount + Number. */ explicit operator rep() const; // round to nearest, even on tie friend constexpr bool operator==(Number const& x, Number const& y) noexcept { return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_; } friend constexpr bool operator!=(Number const& x, Number const& y) noexcept { return !(x == y); } friend constexpr bool operator<(Number const& x, Number const& y) noexcept { // If the two amounts have different signs (zero is treated as positive) // then the comparison is true iff the left is negative. bool const lneg = x.mantissa_ < 0; bool const rneg = y.mantissa_ < 0; if (lneg != rneg) return lneg; // Both have same sign and the left is zero: the right must be // greater than 0. if (x.mantissa_ == 0) return y.mantissa_ > 0; // Both have same sign, the right is zero and the left is non-zero. if (y.mantissa_ == 0) return false; // Both have the same sign, compare by exponents: if (x.exponent_ > y.exponent_) return lneg; if (x.exponent_ < y.exponent_) return !lneg; // If equal exponents, compare mantissas return x.mantissa_ < y.mantissa_; } /** Return the sign of the amount */ constexpr int signum() const noexcept { return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0); } 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(); return ret; } friend constexpr bool operator>(Number const& x, Number const& y) noexcept { return y < x; } friend constexpr bool operator<=(Number const& x, Number const& y) noexcept { return !(y < x); } friend constexpr bool operator>=(Number const& x, Number const& y) noexcept { return !(x < y); } friend std::ostream& operator<<(std::ostream& os, Number const& x) { return os << to_string(x); } // Thread local rounding control. Default is to_nearest enum rounding_mode { to_nearest, towards_zero, downward, upward }; static rounding_mode getround(); // Returns previously set mode static rounding_mode setround(rounding_mode mode); private: static thread_local rounding_mode mode_; void normalize(); constexpr bool isnormal() const noexcept; class Guard; }; inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept : mantissa_{mantissa}, exponent_{exponent} { } inline Number::Number(rep mantissa, int exponent) : mantissa_{mantissa}, exponent_{exponent} { normalize(); } inline Number::Number(rep mantissa) : Number{mantissa, 0} { } inline constexpr Number::rep Number::mantissa() const noexcept { return mantissa_; } inline constexpr int Number::exponent() const noexcept { return exponent_; } inline constexpr Number Number::operator+() const noexcept { return *this; } inline constexpr Number Number::operator-() const noexcept { auto x = *this; x.mantissa_ = -x.mantissa_; return x; } inline Number& Number::operator++() { *this += Number{1000000000000000, -15, unchecked{}}; return *this; } inline Number Number::operator++(int) { auto x = *this; ++(*this); return x; } inline Number& Number::operator--() { *this -= Number{1000000000000000, -15, unchecked{}}; return *this; } inline Number Number::operator--(int) { auto x = *this; --(*this); return x; } inline Number& Number::operator-=(Number const& x) { return *this += -x; } inline Number operator+(Number const& x, Number const& y) { auto z = x; z += y; return z; } inline Number operator-(Number const& x, Number const& y) { auto z = x; z -= y; return z; } inline Number operator*(Number const& x, Number const& y) { auto z = x; z *= y; return z; } inline Number operator/(Number const& x, Number const& y) { auto z = x; z /= y; return z; } inline constexpr Number Number::min() noexcept { return Number{minMantissa, minExponent, unchecked{}}; } inline constexpr Number Number::max() noexcept { return Number{maxMantissa, maxExponent, unchecked{}}; } inline constexpr Number Number::lowest() noexcept { return -Number{maxMantissa, maxExponent, unchecked{}}; } inline constexpr bool Number::isnormal() const noexcept { auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_; return minMantissa <= abs_m && abs_m <= maxMantissa && minExponent <= exponent_ && exponent_ <= maxExponent; } inline constexpr Number abs(Number x) noexcept { if (x < Number{}) x = -x; return x; } // Returns f^n // Uses a log_2(n) number of multiplications Number power(Number const& f, unsigned n); // Returns f^(1/d) // Uses Newton–Raphson iterations until the result stops changing // to find the root of the polynomial g(x) = x^d - f Number root(Number f, unsigned d); Number root2(Number f); // Returns f^(n/d) Number power(Number const& f, unsigned n, unsigned d); // Return 0 if abs(x) < limit, else returns x inline constexpr Number squelch(Number const& x, Number const& limit) noexcept { if (abs(x) < limit) return Number{}; return x; } class saveNumberRoundMode { Number::rounding_mode mode_; public: ~saveNumberRoundMode() { Number::setround(mode_); } explicit saveNumberRoundMode(Number::rounding_mode mode) noexcept : mode_{mode} { } saveNumberRoundMode(saveNumberRoundMode const&) = delete; saveNumberRoundMode& operator=(saveNumberRoundMode const&) = delete; }; // saveNumberRoundMode doesn't do quite enough for us. What we want is a // Number::RoundModeGuard that sets the new mode and restores the old mode // when it leaves scope. Since Number doesn't have that facility, we'll // build it here. class NumberRoundModeGuard { saveNumberRoundMode saved_; public: explicit NumberRoundModeGuard(Number::rounding_mode mode) noexcept : saved_{Number::setround(mode)} { } NumberRoundModeGuard(NumberRoundModeGuard const&) = delete; NumberRoundModeGuard& operator=(NumberRoundModeGuard const&) = delete; }; } // namespace ripple #endif // RIPPLE_BASICS_NUMBER_H_INCLUDED