mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 14:05:51 +00:00
Step 1: Convert Number to use 128-bit numbers internally
- Update the conversion points between Number and *Amount & STNumber. - Tests probably don't pass.
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
#ifndef XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
#define XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
@@ -13,21 +18,66 @@ class Number;
|
||||
std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
template <typename T>
|
||||
constexpr std::optional<int>
|
||||
logTen(T value)
|
||||
{
|
||||
int power = 0;
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
{
|
||||
value /= 10;
|
||||
++power;
|
||||
}
|
||||
if (value == 1)
|
||||
return power;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool
|
||||
isPowerOfTen(T value)
|
||||
{
|
||||
return logTen(value).has_value();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using numberuint128 = boost::multiprecision::uint128_t;
|
||||
using numberint128 = boost::multiprecision::int128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using numberuint128 = __uint128_t;
|
||||
using numberint128 = __int128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
struct MantissaRange
|
||||
{
|
||||
using rep = numberint128;
|
||||
|
||||
explicit constexpr MantissaRange(rep min_)
|
||||
: min(min_), max(min_ * 10 - 1), power(logTen(min).value_or(-1))
|
||||
{
|
||||
}
|
||||
|
||||
rep min;
|
||||
rep max;
|
||||
int power;
|
||||
};
|
||||
|
||||
class Number
|
||||
{
|
||||
using uint128_t = numberuint128;
|
||||
using int128_t = numberint128;
|
||||
|
||||
using rep = std::int64_t;
|
||||
rep mantissa_{0};
|
||||
using internalrep = MantissaRange::rep;
|
||||
internalrep 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;
|
||||
|
||||
// May need to make unchecked private
|
||||
struct unchecked
|
||||
{
|
||||
explicit unchecked() = default;
|
||||
@@ -37,9 +87,12 @@ public:
|
||||
|
||||
Number(rep mantissa);
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
|
||||
explicit constexpr Number(
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept;
|
||||
|
||||
constexpr rep
|
||||
constexpr internalrep
|
||||
mantissa() const noexcept;
|
||||
constexpr int
|
||||
exponent() const noexcept;
|
||||
@@ -68,11 +121,11 @@ public:
|
||||
operator/=(Number const& x);
|
||||
|
||||
static constexpr Number
|
||||
min() noexcept;
|
||||
min(MantissaRange const& range) noexcept;
|
||||
static constexpr Number
|
||||
max() noexcept;
|
||||
max(MantissaRange const& range) noexcept;
|
||||
static constexpr Number
|
||||
lowest() noexcept;
|
||||
lowest(MantissaRange const& range) noexcept;
|
||||
|
||||
/** Conversions to Number are implicit and conversions away from Number
|
||||
* are explicit. This design encourages and facilitates the use of Number
|
||||
@@ -181,18 +234,86 @@ public:
|
||||
static rounding_mode
|
||||
setround(rounding_mode mode);
|
||||
|
||||
static void
|
||||
setLargeMantissa(bool large);
|
||||
|
||||
inline static internalrep
|
||||
minMantissa()
|
||||
{
|
||||
return range_.get().min;
|
||||
}
|
||||
|
||||
inline static internalrep
|
||||
maxMantissa()
|
||||
{
|
||||
return range_.get().max;
|
||||
}
|
||||
|
||||
inline static internalrep
|
||||
mantissaPower()
|
||||
{
|
||||
return range_.get().power;
|
||||
}
|
||||
|
||||
constexpr static Number
|
||||
oneSmall();
|
||||
constexpr static Number
|
||||
oneLarge();
|
||||
|
||||
static Number
|
||||
one();
|
||||
|
||||
template <class T>
|
||||
[[nodiscard]]
|
||||
std::pair<T, int>
|
||||
normalizeToRange(T minMantissa, T maxMantissa) const;
|
||||
|
||||
private:
|
||||
static thread_local rounding_mode mode_;
|
||||
// The available ranges for mantissa
|
||||
|
||||
constexpr static MantissaRange smallRange{1'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(smallRange.min));
|
||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||
// maxint64 9,223,372,036,854,775,808
|
||||
constexpr static MantissaRange largeRange{1'000'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(largeRange.min < std::numeric_limits<std::int64_t>::max());
|
||||
static_assert(largeRange.max > std::numeric_limits<std::int64_t>::max());
|
||||
|
||||
// The range for the mantissa when normalized.
|
||||
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
||||
// changing the values inside the range.
|
||||
static thread_local std::reference_wrapper<MantissaRange const> range_;
|
||||
|
||||
void
|
||||
normalize();
|
||||
|
||||
static void
|
||||
normalize(
|
||||
internalrep& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa);
|
||||
|
||||
constexpr bool
|
||||
isnormal() const noexcept;
|
||||
isnormal(MantissaRange const& range) const noexcept;
|
||||
|
||||
explicit Number(internalrep mantissa, int exponent);
|
||||
|
||||
friend Number
|
||||
root(Number f, unsigned d);
|
||||
friend Number
|
||||
root2(Number f);
|
||||
|
||||
class Guard;
|
||||
};
|
||||
|
||||
inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
|
||||
inline constexpr Number::Number(
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
@@ -203,11 +324,17 @@ inline Number::Number(rep mantissa, int exponent)
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent)
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
{
|
||||
}
|
||||
|
||||
inline constexpr Number::rep
|
||||
inline constexpr Number::internalrep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
return mantissa_;
|
||||
@@ -302,31 +429,42 @@ operator/(Number const& x, Number const& y)
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::min() noexcept
|
||||
Number::min(MantissaRange const& range) noexcept
|
||||
{
|
||||
return Number{minMantissa, minExponent, unchecked{}};
|
||||
return Number{range.min, minExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::max() noexcept
|
||||
Number::max(MantissaRange const& range) noexcept
|
||||
{
|
||||
return Number{maxMantissa, maxExponent, unchecked{}};
|
||||
return Number{range.max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::lowest() noexcept
|
||||
Number::lowest(MantissaRange const& range) noexcept
|
||||
{
|
||||
return -Number{maxMantissa, maxExponent, unchecked{}};
|
||||
return -Number{range.max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr bool
|
||||
Number::isnormal() const noexcept
|
||||
Number::isnormal(MantissaRange const& range) const noexcept
|
||||
{
|
||||
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
|
||||
return minMantissa <= abs_m && abs_m <= maxMantissa &&
|
||||
return range.min <= abs_m && abs_m <= range.max &&
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
||||
{
|
||||
internalrep mantissa = mantissa_;
|
||||
int exponent = exponent_;
|
||||
Number::normalize(mantissa, exponent, minMantissa, maxMantissa);
|
||||
|
||||
return std::make_pair(static_cast<T>(mantissa), exponent);
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
abs(Number x) noexcept
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ toAmount(
|
||||
{
|
||||
if (isXRP(issue))
|
||||
return STAmount(issue, static_cast<std::int64_t>(n));
|
||||
return STAmount(issue, n.mantissa(), n.exponent());
|
||||
return STAmount(issue, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -84,6 +84,12 @@ public:
|
||||
return holds<Issue>() && get<Issue>().native();
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return !holds<Issue>() || get<Issue>().native();
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator==(Asset const& lhs, Asset const& rhs);
|
||||
|
||||
|
||||
@@ -26,8 +26,10 @@ class IOUAmount : private boost::totally_ordered<IOUAmount>,
|
||||
private boost::additive<IOUAmount>
|
||||
{
|
||||
private:
|
||||
std::int64_t mantissa_;
|
||||
int exponent_;
|
||||
using mantissa_type = std::int64_t;
|
||||
using exponent_type = int;
|
||||
mantissa_type mantissa_;
|
||||
exponent_type exponent_;
|
||||
|
||||
/** Adjusts the mantissa and exponent to the proper range.
|
||||
|
||||
@@ -38,11 +40,19 @@ private:
|
||||
void
|
||||
normalize();
|
||||
|
||||
IOUAmount(std::pair<mantissa_type, exponent_type> parts)
|
||||
: IOUAmount(parts.first, parts.second)
|
||||
{
|
||||
}
|
||||
|
||||
static std::pair<mantissa_type, exponent_type>
|
||||
scaleNumber(Number const& number);
|
||||
|
||||
public:
|
||||
IOUAmount() = default;
|
||||
explicit IOUAmount(Number const& other);
|
||||
IOUAmount(beast::Zero);
|
||||
IOUAmount(std::int64_t mantissa, int exponent);
|
||||
IOUAmount(mantissa_type mantissa, exponent_type exponent);
|
||||
|
||||
IOUAmount& operator=(beast::Zero);
|
||||
|
||||
@@ -71,10 +81,10 @@ public:
|
||||
int
|
||||
signum() const noexcept;
|
||||
|
||||
int
|
||||
exponent_type
|
||||
exponent() const noexcept;
|
||||
|
||||
std::int64_t
|
||||
mantissa_type
|
||||
mantissa() const noexcept;
|
||||
|
||||
static IOUAmount
|
||||
@@ -92,7 +102,7 @@ inline IOUAmount::IOUAmount(beast::Zero)
|
||||
*this = beast::zero;
|
||||
}
|
||||
|
||||
inline IOUAmount::IOUAmount(std::int64_t mantissa, int exponent)
|
||||
inline IOUAmount::IOUAmount(mantissa_type mantissa, exponent_type exponent)
|
||||
: mantissa_(mantissa), exponent_(exponent)
|
||||
{
|
||||
normalize();
|
||||
@@ -149,13 +159,13 @@ IOUAmount::signum() const noexcept
|
||||
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
inline int
|
||||
inline IOUAmount::exponent_type
|
||||
IOUAmount::exponent() const noexcept
|
||||
{
|
||||
return exponent_;
|
||||
}
|
||||
|
||||
inline std::int64_t
|
||||
inline IOUAmount::mantissa_type
|
||||
IOUAmount::mantissa() const noexcept
|
||||
{
|
||||
return mantissa_;
|
||||
|
||||
@@ -37,6 +37,9 @@ public:
|
||||
bool
|
||||
native() const;
|
||||
|
||||
bool
|
||||
integral() const;
|
||||
|
||||
friend constexpr std::weak_ordering
|
||||
operator<=>(Issue const& lhs, Issue const& rhs);
|
||||
};
|
||||
|
||||
@@ -46,6 +46,12 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool
|
||||
|
||||
@@ -47,9 +47,11 @@ public:
|
||||
static int const cMaxOffset = 80;
|
||||
|
||||
// Maximum native value supported by the code
|
||||
static std::uint64_t const cMinValue = 1000000000000000ull;
|
||||
static std::uint64_t const cMaxValue = 9999999999999999ull;
|
||||
static std::uint64_t const cMaxNative = 9000000000000000000ull;
|
||||
constexpr static std::uint64_t cMinValue = 1'000'000'000'000'000ull;
|
||||
static_assert(isPowerOfTen(cMinValue));
|
||||
constexpr static std::uint64_t cMaxValue = cMinValue * 10 - 1;
|
||||
static_assert(cMaxValue == 9'999'999'999'999'999ull);
|
||||
static std::uint64_t const cMaxNative = 9'000'000'000'000'000'000ull;
|
||||
|
||||
// Max native value on network.
|
||||
static std::uint64_t const cMaxNativeN = 100000000000000000ull;
|
||||
@@ -136,7 +138,7 @@ public:
|
||||
|
||||
template <AssetType A>
|
||||
STAmount(A const& asset, Number const& number)
|
||||
: STAmount(asset, number.mantissa(), number.exponent())
|
||||
: STAmount(asset, scaleNumber(asset, number))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -155,6 +157,9 @@ public:
|
||||
int
|
||||
exponent() const noexcept;
|
||||
|
||||
bool
|
||||
integral() const noexcept;
|
||||
|
||||
bool
|
||||
native() const noexcept;
|
||||
|
||||
@@ -277,6 +282,22 @@ public:
|
||||
mpt() const;
|
||||
|
||||
private:
|
||||
template <AssetType A>
|
||||
STAmount(
|
||||
A const& asset,
|
||||
std::tuple<mantissa_type, exponent_type, bool> parts)
|
||||
: STAmount(
|
||||
asset,
|
||||
std::get<mantissa_type>(parts),
|
||||
std::get<exponent_type>(parts),
|
||||
std::get<bool>(parts))
|
||||
{
|
||||
}
|
||||
|
||||
template <AssetType A>
|
||||
static std::tuple<mantissa_type, exponent_type, bool>
|
||||
scaleNumber(A const& asset, Number const& number);
|
||||
|
||||
static std::unique_ptr<STAmount>
|
||||
construct(SerialIter&, SField const& name);
|
||||
|
||||
@@ -435,6 +456,12 @@ STAmount::exponent() const noexcept
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
inline bool
|
||||
STAmount::integral() const noexcept
|
||||
{
|
||||
return mAsset.integral();
|
||||
}
|
||||
|
||||
inline bool
|
||||
STAmount::native() const noexcept
|
||||
{
|
||||
@@ -531,12 +558,29 @@ STAmount::operator=(XRPAmount const& amount)
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <AssetType A>
|
||||
inline std::tuple<STAmount::mantissa_type, STAmount::exponent_type, bool>
|
||||
STAmount::scaleNumber(A const& asset, Number const& number)
|
||||
{
|
||||
bool const negative = number.mantissa() < 0;
|
||||
if (asset.integral())
|
||||
{
|
||||
return std::make_tuple(std::int64_t(number), 0, negative);
|
||||
}
|
||||
else
|
||||
{
|
||||
Number const working{negative ? -number : number};
|
||||
auto const [mantissa, exponent] =
|
||||
working.normalizeToRange(cMinValue, cMaxValue);
|
||||
|
||||
return std::make_tuple(mantissa, exponent, negative);
|
||||
}
|
||||
}
|
||||
|
||||
inline STAmount&
|
||||
STAmount::operator=(Number const& number)
|
||||
{
|
||||
mIsNegative = number.mantissa() < 0;
|
||||
mValue = mIsNegative ? -number.mantissa() : number.mantissa();
|
||||
mOffset = number.exponent();
|
||||
std::tie(mValue, mOffset, mIsNegative) = scaleNumber(mAsset, number);
|
||||
canonicalize();
|
||||
return *this;
|
||||
}
|
||||
@@ -553,7 +597,7 @@ STAmount::clear()
|
||||
{
|
||||
// The -100 is used to allow 0 to sort less than a small positive values
|
||||
// which have a negative exponent.
|
||||
mOffset = native() ? 0 : -100;
|
||||
mOffset = integral() ? 0 : -100;
|
||||
mValue = 0;
|
||||
mIsNegative = false;
|
||||
}
|
||||
|
||||
@@ -482,6 +482,8 @@ public:
|
||||
value_type
|
||||
operator*() const;
|
||||
|
||||
/// Do not use operator->() unless the field is required, or you've checked
|
||||
/// that it's set.
|
||||
T const*
|
||||
operator->() const;
|
||||
|
||||
@@ -718,6 +720,8 @@ STObject::Proxy<T>::operator*() const -> value_type
|
||||
return this->value();
|
||||
}
|
||||
|
||||
/// Do not use operator->() unless the field is required, or you've checked that
|
||||
/// it's set.
|
||||
template <class T>
|
||||
T const*
|
||||
STObject::Proxy<T>::operator->() const
|
||||
|
||||
@@ -23,6 +23,7 @@ systemName()
|
||||
|
||||
/** Number of drops in the genesis account. */
|
||||
constexpr XRPAmount INITIAL_XRP{100'000'000'000 * DROPS_PER_XRP};
|
||||
static_assert(INITIAL_XRP.drops() == 100'000'000'000'000'000);
|
||||
|
||||
/** Returns true if the amount does not exceed the initial XRP in existence. */
|
||||
inline bool
|
||||
|
||||
@@ -479,10 +479,10 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfData, soeOPTIONAL},
|
||||
{sfAsset, soeREQUIRED},
|
||||
{sfAssetsTotal, soeREQUIRED},
|
||||
{sfAssetsAvailable, soeREQUIRED},
|
||||
{sfAssetsTotal, soeDEFAULT},
|
||||
{sfAssetsAvailable, soeDEFAULT},
|
||||
{sfAssetsMaximum, soeDEFAULT},
|
||||
{sfLossUnrealized, soeREQUIRED},
|
||||
{sfLossUnrealized, soeDEFAULT},
|
||||
{sfShareMPTID, soeREQUIRED},
|
||||
{sfWithdrawalPolicy, soeREQUIRED},
|
||||
{sfScale, soeDEFAULT},
|
||||
|
||||
@@ -14,15 +14,14 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("Using boost::multiprecision::uint128_t")
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
using uint128_t = boost::multiprecision::uint128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using uint128_t = __uint128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
#endif
|
||||
|
||||
namespace ripple {
|
||||
|
||||
thread_local Number::rounding_mode Number::mode_ = Number::to_nearest;
|
||||
// TODO: Once the Rules switching is implemented, default to largeRange
|
||||
thread_local std::reference_wrapper<MantissaRange const> Number::range_ =
|
||||
smallRange; // largeRange;
|
||||
|
||||
Number::rounding_mode
|
||||
Number::getround()
|
||||
@@ -63,7 +62,7 @@ public:
|
||||
|
||||
// add a digit
|
||||
void
|
||||
push(unsigned d) noexcept;
|
||||
push(numberuint128 d) noexcept;
|
||||
|
||||
// recover a digit
|
||||
unsigned
|
||||
@@ -95,8 +94,10 @@ Number::Guard::is_negative() const noexcept
|
||||
}
|
||||
|
||||
inline void
|
||||
Number::Guard::push(unsigned d) noexcept
|
||||
Number::Guard::push(numberuint128 d128) noexcept
|
||||
{
|
||||
unsigned d = static_cast<unsigned>(d128);
|
||||
|
||||
xbit_ = xbit_ || (digits_ & 0x0000'0000'0000'000F) != 0;
|
||||
digits_ >>= 4;
|
||||
digits_ |= (d & 0x0000'0000'0000'000FULL) << 60;
|
||||
@@ -153,14 +154,42 @@ Number::Guard::round() noexcept
|
||||
|
||||
// Number
|
||||
|
||||
constexpr Number one{1000000000000000, -15, Number::unchecked{}};
|
||||
constexpr Number
|
||||
Number::oneSmall()
|
||||
{
|
||||
return Number{
|
||||
Number::smallRange.min, -Number::smallRange.power, Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number
|
||||
Number::oneLarge()
|
||||
{
|
||||
return Number{
|
||||
Number::largeRange.min, -Number::largeRange.power, Number::unchecked{}};
|
||||
};
|
||||
|
||||
Number
|
||||
Number::one()
|
||||
{
|
||||
if (&range_.get() == &smallRange)
|
||||
return oneSmall();
|
||||
XRPL_ASSERT(&range_.get() == &largeRange, "Number::one() : valid range_");
|
||||
return oneLarge();
|
||||
}
|
||||
|
||||
// Use the member names in this static function for now so the diff is cleaner
|
||||
void
|
||||
Number::normalize()
|
||||
Number::normalize(
|
||||
internalrep& mantissa_,
|
||||
int& exponent_,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa)
|
||||
{
|
||||
if (mantissa_ == 0)
|
||||
{
|
||||
*this = Number{};
|
||||
constexpr Number zero = Number{};
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
return;
|
||||
}
|
||||
bool const negative = (mantissa_ < 0);
|
||||
@@ -186,7 +215,9 @@ Number::normalize()
|
||||
mantissa_ = m;
|
||||
if ((exponent_ < minExponent) || (mantissa_ < minMantissa))
|
||||
{
|
||||
*this = Number{};
|
||||
constexpr Number zero = Number{};
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,6 +238,13 @@ Number::normalize()
|
||||
mantissa_ = -mantissa_;
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize()
|
||||
{
|
||||
normalize(
|
||||
mantissa_, exponent_, Number::minMantissa(), Number::maxMantissa());
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator+=(Number const& y)
|
||||
{
|
||||
@@ -223,7 +261,7 @@ Number::operator+=(Number const& y)
|
||||
return *this;
|
||||
}
|
||||
XRPL_ASSERT(
|
||||
isnormal() && y.isnormal(),
|
||||
isnormal(Number::range_) && y.isnormal(Number::range_),
|
||||
"ripple::Number::operator+=(Number) : is normal");
|
||||
auto xm = mantissa();
|
||||
auto xe = exponent();
|
||||
@@ -266,6 +304,8 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
if (xn == yn)
|
||||
{
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
|
||||
xm += ym;
|
||||
if (xm > maxMantissa)
|
||||
{
|
||||
@@ -288,6 +328,8 @@ Number::operator+=(Number const& y)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
|
||||
if (xm > ym)
|
||||
{
|
||||
xm = xm - ym;
|
||||
@@ -332,7 +374,7 @@ Number::operator+=(Number const& y)
|
||||
// Derived from Hacker's Delight Second Edition Chapter 10
|
||||
// by Henry S. Warren, Jr.
|
||||
static inline unsigned
|
||||
divu10(uint128_t& u)
|
||||
divu10(numberuint128& u)
|
||||
{
|
||||
// q = u * 0.75
|
||||
auto q = (u >> 1) + (u >> 2);
|
||||
@@ -364,7 +406,7 @@ Number::operator*=(Number const& y)
|
||||
return *this;
|
||||
}
|
||||
XRPL_ASSERT(
|
||||
isnormal() && y.isnormal(),
|
||||
isnormal(Number::range_) && y.isnormal(Number::range_),
|
||||
"ripple::Number::operator*=(Number) : is normal");
|
||||
auto xm = mantissa();
|
||||
auto xe = exponent();
|
||||
@@ -382,12 +424,15 @@ Number::operator*=(Number const& y)
|
||||
ym = -ym;
|
||||
yn = -1;
|
||||
}
|
||||
auto zm = uint128_t(xm) * uint128_t(ym);
|
||||
auto zm = numberuint128(xm) * numberuint128(ym);
|
||||
auto ze = xe + ye;
|
||||
auto zn = xn * yn;
|
||||
Guard g;
|
||||
if (zn == -1)
|
||||
g.set_negative();
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
|
||||
while (zm > maxMantissa)
|
||||
{
|
||||
// The following is optimization for:
|
||||
@@ -420,7 +465,7 @@ Number::operator*=(Number const& y)
|
||||
mantissa_ = xm * zn;
|
||||
exponent_ = xe;
|
||||
XRPL_ASSERT(
|
||||
isnormal() || *this == Number{},
|
||||
isnormal(Number::range_) || *this == Number{},
|
||||
"ripple::Number::operator*=(Number) : result is normal");
|
||||
return *this;
|
||||
}
|
||||
@@ -448,10 +493,13 @@ Number::operator/=(Number const& y)
|
||||
dm = -dm;
|
||||
dp = -1;
|
||||
}
|
||||
// Shift by 10^17 gives greatest precision while not overflowing uint128_t
|
||||
// or the cast back to int64_t
|
||||
uint128_t const f = 100'000'000'000'000'000;
|
||||
mantissa_ = static_cast<std::int64_t>(uint128_t(nm) * f / uint128_t(dm));
|
||||
// Shift by 10^17 gives greatest precision while not overflowing
|
||||
// numberuint128 or the cast back to int64_t
|
||||
// TODO: Can/should this be made bigger for largeRange?
|
||||
constexpr numberuint128 f = 100'000'000'000'000'000;
|
||||
static_assert(f == smallRange.min * 100);
|
||||
static_assert(smallRange.power == 15);
|
||||
mantissa_ = numberuint128(nm) * f / numberuint128(dm);
|
||||
exponent_ = ne - de - 17;
|
||||
mantissa_ *= np * dp;
|
||||
normalize();
|
||||
@@ -460,7 +508,7 @@ Number::operator/=(Number const& y)
|
||||
|
||||
Number::operator rep() const
|
||||
{
|
||||
rep drops = mantissa_;
|
||||
internalrep drops = mantissa_;
|
||||
int offset = exponent_;
|
||||
Guard g;
|
||||
if (drops != 0)
|
||||
@@ -477,10 +525,12 @@ Number::operator rep() const
|
||||
}
|
||||
for (; offset > 0; --offset)
|
||||
{
|
||||
if (drops > std::numeric_limits<decltype(drops)>::max() / 10)
|
||||
if (drops > std::numeric_limits<rep>::max() / 10)
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
drops *= 10;
|
||||
}
|
||||
if (drops > std::numeric_limits<rep>::max() / 10)
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
auto r = g.round();
|
||||
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
||||
{
|
||||
@@ -489,7 +539,7 @@ Number::operator rep() const
|
||||
if (g.is_negative())
|
||||
drops = -drops;
|
||||
}
|
||||
return drops;
|
||||
return static_cast<rep>(drops);
|
||||
}
|
||||
|
||||
std::string
|
||||
@@ -503,9 +553,11 @@ to_string(Number const& amount)
|
||||
auto mantissa = amount.mantissa();
|
||||
|
||||
// Use scientific notation for exponents that are too small or too large
|
||||
if (((exponent != 0) && ((exponent < -25) || (exponent > -5))))
|
||||
auto const rangePower = Number::mantissaPower();
|
||||
if (((exponent != 0) &&
|
||||
((exponent < -(rangePower + 10)) || (exponent > -(rangePower - 10)))))
|
||||
{
|
||||
std::string ret = std::to_string(mantissa);
|
||||
std::string ret = to_string(mantissa);
|
||||
ret.append(1, 'e');
|
||||
ret.append(std::to_string(exponent));
|
||||
return ret;
|
||||
@@ -525,7 +577,7 @@ to_string(Number const& amount)
|
||||
ptrdiff_t const pad_prefix = 27;
|
||||
ptrdiff_t const pad_suffix = 23;
|
||||
|
||||
std::string const raw_value(std::to_string(mantissa));
|
||||
std::string const raw_value(to_string(mantissa));
|
||||
std::string val;
|
||||
|
||||
val.reserve(raw_value.length() + pad_prefix + pad_suffix);
|
||||
@@ -594,7 +646,7 @@ Number
|
||||
power(Number const& f, unsigned n)
|
||||
{
|
||||
if (n == 0)
|
||||
return one;
|
||||
return Number::one();
|
||||
if (n == 1)
|
||||
return f;
|
||||
auto r = power(f, n / 2);
|
||||
@@ -616,6 +668,8 @@ power(Number const& f, unsigned n)
|
||||
Number
|
||||
root(Number f, unsigned d)
|
||||
{
|
||||
auto const one = Number::one();
|
||||
|
||||
if (f == one || d == 1)
|
||||
return f;
|
||||
if (d == 0)
|
||||
@@ -681,6 +735,8 @@ root(Number f, unsigned d)
|
||||
Number
|
||||
root2(Number f)
|
||||
{
|
||||
auto const one = Number::one();
|
||||
|
||||
if (f == one)
|
||||
return f;
|
||||
if (f < Number{})
|
||||
@@ -721,6 +777,8 @@ root2(Number f)
|
||||
Number
|
||||
power(Number const& f, unsigned n, unsigned d)
|
||||
{
|
||||
auto const one = Number::one();
|
||||
|
||||
if (f == one)
|
||||
return f;
|
||||
auto g = std::gcd(n, d);
|
||||
|
||||
@@ -40,12 +40,18 @@ setSTNumberSwitchover(bool v)
|
||||
}
|
||||
|
||||
/* The range for the mantissa when normalized */
|
||||
static std::int64_t constexpr minMantissa = 1000000000000000ull;
|
||||
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
|
||||
static std::int64_t constexpr minMantissa = 1'000'000'000'000'000ull;
|
||||
static std::int64_t constexpr maxMantissa = minMantissa * 10 - 1;
|
||||
/* The range for the exponent when normalized */
|
||||
static int constexpr minExponent = -96;
|
||||
static int constexpr maxExponent = 80;
|
||||
|
||||
std::pair<IOUAmount::mantissa_type, IOUAmount::exponent_type>
|
||||
IOUAmount::scaleNumber(Number const& number)
|
||||
{
|
||||
return number.normalizeToRange(minMantissa, maxMantissa);
|
||||
}
|
||||
|
||||
IOUAmount
|
||||
IOUAmount::minPositiveAmount()
|
||||
{
|
||||
@@ -64,8 +70,7 @@ IOUAmount::normalize()
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
Number const v{mantissa_, exponent_};
|
||||
mantissa_ = v.mantissa();
|
||||
exponent_ = v.exponent();
|
||||
std::tie(mantissa_, exponent_) = scaleNumber(v);
|
||||
if (exponent_ > maxExponent)
|
||||
Throw<std::overflow_error>("value overflow");
|
||||
if (exponent_ < minExponent)
|
||||
@@ -106,8 +111,7 @@ IOUAmount::normalize()
|
||||
mantissa_ = -mantissa_;
|
||||
}
|
||||
|
||||
IOUAmount::IOUAmount(Number const& other)
|
||||
: mantissa_(other.mantissa()), exponent_(other.exponent())
|
||||
IOUAmount::IOUAmount(Number const& other) : IOUAmount(scaleNumber(other))
|
||||
{
|
||||
if (exponent_ > maxExponent)
|
||||
Throw<std::overflow_error>("value overflow");
|
||||
|
||||
@@ -49,6 +49,12 @@ Issue::native() const
|
||||
return *this == xrpIssue();
|
||||
}
|
||||
|
||||
bool
|
||||
Issue::integral() const
|
||||
{
|
||||
return native();
|
||||
}
|
||||
|
||||
bool
|
||||
isConsistent(Issue const& ac)
|
||||
{
|
||||
|
||||
@@ -1323,7 +1323,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
auto const r = Number{v1} * Number{v2};
|
||||
return STAmount{asset, r.mantissa(), r.exponent()};
|
||||
return STAmount{asset, r};
|
||||
}
|
||||
|
||||
std::uint64_t value1 = v1.mantissa();
|
||||
|
||||
@@ -50,8 +50,11 @@ STNumber::add(Serializer& s) const
|
||||
XRPL_ASSERT(
|
||||
getFName().fieldType == getSType(),
|
||||
"ripple::STNumber::add : field type match");
|
||||
s.add64(value_.mantissa());
|
||||
s.add32(value_.exponent());
|
||||
constexpr std::int64_t min = 100'000'000'000'000'000LL;
|
||||
constexpr std::int64_t max = min * 10 - 1;
|
||||
auto const [mantissa, exponent] = value_.normalizeToRange(min, max);
|
||||
s.add64(mantissa);
|
||||
s.add32(exponent);
|
||||
}
|
||||
|
||||
Number const&
|
||||
|
||||
@@ -1384,15 +1384,14 @@ private:
|
||||
// equal asset deposit: unit test to exercise the rounding-down of
|
||||
// LPTokens in the AMMHelpers.cpp: adjustLPTokens calculations
|
||||
// The LPTokens need to have 16 significant digits and a fractional part
|
||||
for (Number const deltaLPTokens :
|
||||
for (Number const& deltaLPTokens :
|
||||
{Number{UINT64_C(100000'0000000009), -10},
|
||||
Number{UINT64_C(100000'0000000001), -10}})
|
||||
{
|
||||
testAMM([&](AMM& ammAlice, Env& env) {
|
||||
// initial LPToken balance
|
||||
IOUAmount const initLPToken = ammAlice.getLPTokensBalance();
|
||||
IOUAmount const newLPTokens{
|
||||
deltaLPTokens.mantissa(), deltaLPTokens.exponent()};
|
||||
IOUAmount const newLPTokens{deltaLPTokens};
|
||||
|
||||
// carol performs a two-asset deposit
|
||||
ammAlice.deposit(
|
||||
@@ -1417,11 +1416,9 @@ private:
|
||||
Number const deltaXRP = fr * 1e10;
|
||||
Number const deltaUSD = fr * 1e4;
|
||||
|
||||
STAmount const depositUSD =
|
||||
STAmount{USD, deltaUSD.mantissa(), deltaUSD.exponent()};
|
||||
STAmount const depositUSD = STAmount{USD, deltaUSD};
|
||||
|
||||
STAmount const depositXRP =
|
||||
STAmount{XRP, deltaXRP.mantissa(), deltaXRP.exponent()};
|
||||
STAmount const depositXRP = STAmount{XRP, deltaXRP};
|
||||
|
||||
// initial LPTokens (1e7) + newLPTokens
|
||||
BEAST_EXPECT(ammAlice.expectBalances(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
@@ -685,19 +686,19 @@ public:
|
||||
Issue const issue;
|
||||
Number const n{7'518'783'80596, -5};
|
||||
saveNumberRoundMode const save{Number::setround(Number::to_nearest)};
|
||||
auto res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
auto res2 = STAmount{issue, n};
|
||||
BEAST_EXPECT(res2 == STAmount{7518784});
|
||||
|
||||
Number::setround(Number::towards_zero);
|
||||
res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
res2 = STAmount{issue, n};
|
||||
BEAST_EXPECT(res2 == STAmount{7518783});
|
||||
|
||||
Number::setround(Number::downward);
|
||||
res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
res2 = STAmount{issue, n};
|
||||
BEAST_EXPECT(res2 == STAmount{7518783});
|
||||
|
||||
Number::setround(Number::upward);
|
||||
res2 = STAmount{issue, n.mantissa(), n.exponent()};
|
||||
res2 = STAmount{issue, n};
|
||||
BEAST_EXPECT(res2 == STAmount{7518784});
|
||||
}
|
||||
|
||||
@@ -725,6 +726,33 @@ public:
|
||||
BEAST_EXPECT(Number(-100, -30000).truncate() == Number(0, 0));
|
||||
}
|
||||
|
||||
void
|
||||
testInt64()
|
||||
{
|
||||
testcase("std::int64_t");
|
||||
|
||||
// Control case
|
||||
BEAST_EXPECT(Number::maxMantissa() > 10);
|
||||
Number ten{10};
|
||||
BEAST_EXPECT(ten.exponent() <= 0);
|
||||
|
||||
BEAST_EXPECT(
|
||||
std::numeric_limits<std::int64_t>::max() > INITIAL_XRP.drops());
|
||||
BEAST_EXPECT(Number::maxMantissa() > INITIAL_XRP.drops());
|
||||
Number initalXrp{INITIAL_XRP};
|
||||
BEAST_EXPECT(initalXrp.exponent() <= 0);
|
||||
|
||||
Number maxInt64{std::numeric_limits<std::int64_t>::max()};
|
||||
BEAST_EXPECT(maxInt64.exponent() <= 0);
|
||||
|
||||
using namespace boost::multiprecision;
|
||||
// maxint64 9,223,372,036,854,775,808
|
||||
int128_t minMantissa{1'000'000'000'000'000'000LL};
|
||||
int128_t maxMantissa{minMantissa * 10 - 1};
|
||||
BEAST_EXPECT(minMantissa < std::numeric_limits<std::int64_t>::max());
|
||||
BEAST_EXPECT(maxMantissa > std::numeric_limits<std::int64_t>::max());
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -746,6 +774,7 @@ public:
|
||||
test_inc_dec();
|
||||
test_toSTAmount();
|
||||
test_truncate();
|
||||
testInt64();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -192,6 +192,12 @@ public:
|
||||
return to_json(asset_);
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return asset_.integral();
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
PrettyAmount
|
||||
operator()(T v, Number::rounding_mode rounding = Number::getround()) const
|
||||
@@ -242,6 +248,12 @@ struct XRP_t
|
||||
return xrpIssue();
|
||||
}
|
||||
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns an amount of XRP as PrettyAmount,
|
||||
which is trivially convertable to STAmount
|
||||
|
||||
@@ -366,6 +378,11 @@ public:
|
||||
{
|
||||
return issue();
|
||||
}
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return issue().integral();
|
||||
}
|
||||
|
||||
/** Implicit conversion to Issue or Asset.
|
||||
|
||||
@@ -456,6 +473,11 @@ public:
|
||||
{
|
||||
return mptIssue();
|
||||
}
|
||||
bool
|
||||
integral() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Implicit conversion to MPTIssue or asset.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user