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