mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-18 18:15:50 +00:00
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d). This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
410 lines
8.5 KiB
C++
410 lines
8.5 KiB
C++
#ifndef XRPL_BASICS_NUMBER_H_INCLUDED
|
||
#define XRPL_BASICS_NUMBER_H_INCLUDED
|
||
|
||
#include <cstdint>
|
||
#include <limits>
|
||
#include <ostream>
|
||
#include <string>
|
||
|
||
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<int>::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 // XRPL_BASICS_NUMBER_H_INCLUDED
|