mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-11 01:12:32 +00:00
Compare commits
17 Commits
develop
...
ximinez/nu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06ff77458a | ||
|
|
f19ecb3b80 | ||
|
|
cc2406bf3f | ||
|
|
30c65320e4 | ||
|
|
569d9ea94e | ||
|
|
02b7bcfa2b | ||
|
|
07c0c320a7 | ||
|
|
d57e37c34b | ||
|
|
154bb65c35 | ||
|
|
111eda22e9 | ||
|
|
f7b6834d2a | ||
|
|
e464adaee6 | ||
|
|
cca92dedca | ||
|
|
3d6f57a4df | ||
|
|
fc29fbe946 | ||
|
|
5e0a8d5c8a | ||
|
|
d27788f12a |
@@ -9,6 +9,10 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <boost/multiprecision/cpp_int.hpp>
|
||||||
|
#endif // !defined(_MSC_VER)
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
class Number;
|
class Number;
|
||||||
@@ -16,18 +20,37 @@ class Number;
|
|||||||
std::string
|
std::string
|
||||||
to_string(Number const& amount);
|
to_string(Number const& amount);
|
||||||
|
|
||||||
|
/** Returns a rough estimate of log10(value).
|
||||||
|
*
|
||||||
|
* The return value is a pair (log, rem), where log is the estimated log10,
|
||||||
|
* and rem is value divided by 10^log. If rem is 1, then value is an exact
|
||||||
|
* power of ten, and log is the exact log10(value).
|
||||||
|
*
|
||||||
|
* This function only works for positive values.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::pair<int, T>
|
||||||
|
logTenEstimate(T value)
|
||||||
|
{
|
||||||
|
int log = 0;
|
||||||
|
T remainder = value;
|
||||||
|
while (value >= 10)
|
||||||
|
{
|
||||||
|
if (value % 10 == 0)
|
||||||
|
remainder = remainder / 10;
|
||||||
|
value /= 10;
|
||||||
|
++log;
|
||||||
|
}
|
||||||
|
return {log, remainder};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr std::optional<int>
|
constexpr std::optional<int>
|
||||||
logTen(T value)
|
logTen(T value)
|
||||||
{
|
{
|
||||||
int log = 0;
|
auto const est = logTenEstimate(value);
|
||||||
while (value >= 10 && value % 10 == 0)
|
if (est.second == 1)
|
||||||
{
|
return est.first;
|
||||||
value /= 10;
|
|
||||||
++log;
|
|
||||||
}
|
|
||||||
if (value == 1)
|
|
||||||
return log;
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,12 +64,10 @@ isPowerOfTen(T value)
|
|||||||
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
||||||
*
|
*
|
||||||
* The mantissa is in the range [min, max], where
|
* The mantissa is in the range [min, max], where
|
||||||
* * min is a power of 10, and
|
|
||||||
* * max = min * 10 - 1.
|
|
||||||
*
|
*
|
||||||
* The mantissa_scale enum indicates whether the range is "small" or "large".
|
* The mantissa_scale enum indicates whether the range is "small" or "large".
|
||||||
* This intentionally restricts the number of MantissaRanges that can be
|
* This intentionally restricts the number of MantissaRanges that can be
|
||||||
* instantiated to two: one for each scale.
|
* used to two: one for each scale.
|
||||||
*
|
*
|
||||||
* The "small" scale is based on the behavior of STAmount for IOUs. It has a min
|
* The "small" scale is based on the behavior of STAmount for IOUs. It has a min
|
||||||
* value of 10^15, and a max value of 10^16-1. This was sufficient for
|
* value of 10^15, and a max value of 10^16-1. This was sufficient for
|
||||||
@@ -60,8 +81,8 @@ isPowerOfTen(T value)
|
|||||||
* "large" scale.
|
* "large" scale.
|
||||||
*
|
*
|
||||||
* The "large" scale is intended to represent all values that can be represented
|
* The "large" scale is intended to represent all values that can be represented
|
||||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max
|
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 2^63/10+1
|
||||||
* value of 10^19-1.
|
* (truncated), and a max value of 2^63-1.
|
||||||
*
|
*
|
||||||
* Note that if the mentioned amendments are eventually retired, this class
|
* Note that if the mentioned amendments are eventually retired, this class
|
||||||
* should be left in place, but the "small" scale option should be removed. This
|
* should be left in place, but the "small" scale option should be removed. This
|
||||||
@@ -73,25 +94,50 @@ struct MantissaRange
|
|||||||
enum mantissa_scale { small, large };
|
enum mantissa_scale { small, large };
|
||||||
|
|
||||||
explicit constexpr MantissaRange(mantissa_scale scale_)
|
explicit constexpr MantissaRange(mantissa_scale scale_)
|
||||||
: min(getMin(scale_)), max(min * 10 - 1), log(logTen(min).value_or(-1)), scale(scale_)
|
: max(getMax(scale_))
|
||||||
|
, min(computeMin(max))
|
||||||
|
, referenceMin(getReferenceMin(scale_, min))
|
||||||
|
, log(computeLog(min))
|
||||||
|
, scale(scale_)
|
||||||
{
|
{
|
||||||
|
// Since this is constexpr, if any of these throw, it won't compile
|
||||||
|
if (min * 10 <= max)
|
||||||
|
throw std::out_of_range("min * 10 <= max");
|
||||||
|
if (max / 10 >= min)
|
||||||
|
throw std::out_of_range("max / 10 >= min");
|
||||||
|
if ((min - 1) * 10 > max)
|
||||||
|
throw std::out_of_range("(min - 1) * 10 > max");
|
||||||
|
// This is a little hacky
|
||||||
|
if ((max + 10) / 10 < min)
|
||||||
|
throw std::out_of_range("(max + 10) / 10 < min");
|
||||||
}
|
}
|
||||||
|
|
||||||
rep min;
|
// Explicitly delete copy and move operations
|
||||||
|
MantissaRange(MantissaRange const&) = delete;
|
||||||
|
MantissaRange(MantissaRange&&) = delete;
|
||||||
|
MantissaRange&
|
||||||
|
operator=(MantissaRange const&) = delete;
|
||||||
|
MantissaRange&
|
||||||
|
operator=(MantissaRange&&) = delete;
|
||||||
|
|
||||||
rep max;
|
rep max;
|
||||||
|
rep min;
|
||||||
|
// This is not a great name. Used to determine if mantissas are in range,
|
||||||
|
// but have fewer digits than max
|
||||||
|
rep referenceMin;
|
||||||
int log;
|
int log;
|
||||||
mantissa_scale scale;
|
mantissa_scale scale;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr rep
|
static constexpr rep
|
||||||
getMin(mantissa_scale scale_)
|
getMax(mantissa_scale scale)
|
||||||
{
|
{
|
||||||
switch (scale_)
|
switch (scale)
|
||||||
{
|
{
|
||||||
case small:
|
case small:
|
||||||
return 1'000'000'000'000'000ULL;
|
return 9'999'999'999'999'999ULL;
|
||||||
case large:
|
case large:
|
||||||
return 1'000'000'000'000'000'000ULL;
|
return std::numeric_limits<std::int64_t>::max();
|
||||||
default:
|
default:
|
||||||
// Since this can never be called outside a non-constexpr
|
// Since this can never be called outside a non-constexpr
|
||||||
// context, this throw assures that the build fails if an
|
// context, this throw assures that the build fails if an
|
||||||
@@ -99,19 +145,59 @@ private:
|
|||||||
throw std::runtime_error("Unknown mantissa scale");
|
throw std::runtime_error("Unknown mantissa scale");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr rep
|
||||||
|
computeMin(rep max)
|
||||||
|
{
|
||||||
|
return max / 10 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr rep
|
||||||
|
getReferenceMin(mantissa_scale scale, rep min)
|
||||||
|
{
|
||||||
|
switch (scale)
|
||||||
|
{
|
||||||
|
case large:
|
||||||
|
return 1'000'000'000'000'000'000ULL;
|
||||||
|
default:
|
||||||
|
if (isPowerOfTen(min))
|
||||||
|
return min;
|
||||||
|
throw std::runtime_error("Unknown/bad mantissa scale");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr rep
|
||||||
|
computeLog(rep min)
|
||||||
|
{
|
||||||
|
auto const estimate = logTenEstimate(min);
|
||||||
|
return estimate.first + (estimate.second == 1 ? 0 : 1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Like std::integral, but only 64-bit integral types.
|
// Like std::integral, but only 64-bit integral types.
|
||||||
template <class T>
|
template <class T>
|
||||||
concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
using uint128_t = boost::multiprecision::uint128_t;
|
||||||
|
using int128_t = boost::multiprecision::int128_t;
|
||||||
|
#else // !defined(_MSC_VER)
|
||||||
|
using uint128_t = __uint128_t;
|
||||||
|
using int128_t = __int128_t;
|
||||||
|
#endif // !defined(_MSC_VER)
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
concept UnsignedMantissa = std::is_unsigned_v<T> || std::is_same_v<T, uint128_t>;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
/** Number is a floating point type that can represent a wide range of values.
|
/** Number is a floating point type that can represent a wide range of values.
|
||||||
*
|
*
|
||||||
* It can represent all values that can be represented by an STAmount -
|
* It can represent all values that can be represented by an STAmount -
|
||||||
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
||||||
* as much precision as those types require.
|
* as much precision as those types require.
|
||||||
*
|
*
|
||||||
* ---- Internal Representation ----
|
* ---- Internal Operational Representation ----
|
||||||
*
|
*
|
||||||
* Internally, Number is represented with three values:
|
* Internally, Number is represented with three values:
|
||||||
* 1. a bool sign flag,
|
* 1. a bool sign flag,
|
||||||
@@ -126,15 +212,21 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
|||||||
*
|
*
|
||||||
* A non-zero mantissa is (almost) always normalized, meaning it and the
|
* A non-zero mantissa is (almost) always normalized, meaning it and the
|
||||||
* exponent are grown or shrunk until the mantissa is in the range
|
* exponent are grown or shrunk until the mantissa is in the range
|
||||||
* [MantissaRange.min, MantissaRange.max].
|
* [MantissaRange.referenceMin, MantissaRange.referenceMin * 10 - 1].
|
||||||
|
*
|
||||||
|
* This internal representation is only used during some operations to ensure
|
||||||
|
* that the mantissa is a known, predictable size. The class itself stores the
|
||||||
|
* values using the external representation described below.
|
||||||
*
|
*
|
||||||
* Note:
|
* Note:
|
||||||
* 1. Normalization can be disabled by using the "unchecked" ctor tag. This
|
* 1. Normalization can be disabled by using the "unchecked" ctor tag. This
|
||||||
* should only be used at specific conversion points, some constexpr
|
* should only be used at specific conversion points, some constexpr
|
||||||
* values, and in unit tests.
|
* values, and in unit tests.
|
||||||
* 2. The max of the "large" range, 10^19-1, is the largest 10^X-1 value that
|
* 2. Unlike MantissaRange.min, referenceMin is always an exact power of 10,
|
||||||
* fits in an unsigned 64-bit number. (10^19-1 < 2^64-1 and
|
* so a mantissa in the internal representation will always have a
|
||||||
* 10^20-1 > 2^64-1). This avoids under- and overflows.
|
* consistent number of digits.
|
||||||
|
* 3. The functions toInternal() and fromInternal() are used to convert
|
||||||
|
* between the two representations.
|
||||||
*
|
*
|
||||||
* ---- External Interface ----
|
* ---- External Interface ----
|
||||||
*
|
*
|
||||||
@@ -147,13 +239,12 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
|||||||
* represent the full range of valid XRP and MPT integer values accurately.
|
* represent the full range of valid XRP and MPT integer values accurately.
|
||||||
*
|
*
|
||||||
* Note:
|
* Note:
|
||||||
* 1. 2^63-1 is between 10^18 and 10^19-1, which are the limits of the "large"
|
* 1. The "large" mantissa range is (2^63/10+1) to 2^63-1. 2^63-1 is between
|
||||||
* mantissa range.
|
* 10^18 and 10^19-1, and (2^63/10+1) is between 10^17 and 10^18-1. Thus,
|
||||||
|
* the mantissa may have 18 or 19 digits. This value will be modified to
|
||||||
|
* always have 19 digits before some operations to ensure consistency.
|
||||||
* 2. The functions mantissa() and exponent() return the external view of the
|
* 2. The functions mantissa() and exponent() return the external view of the
|
||||||
* Number value, specifically using a signed 63-bit mantissa. This may
|
* Number value, specifically using a signed 63-bit mantissa.
|
||||||
* require altering the internal representation to fit into that range
|
|
||||||
* before the value is returned. The interface guarantees consistency of
|
|
||||||
* the two values.
|
|
||||||
* 3. Number cannot represent -2^63 (std::numeric_limits<std::int64_t>::min())
|
* 3. Number cannot represent -2^63 (std::numeric_limits<std::int64_t>::min())
|
||||||
* as an exact integer, but it doesn't need to, because all asset values
|
* as an exact integer, but it doesn't need to, because all asset values
|
||||||
* on-ledger are non-negative. This is due to implementation details of
|
* on-ledger are non-negative. This is due to implementation details of
|
||||||
@@ -208,8 +299,7 @@ class Number
|
|||||||
using rep = std::int64_t;
|
using rep = std::int64_t;
|
||||||
using internalrep = MantissaRange::rep;
|
using internalrep = MantissaRange::rep;
|
||||||
|
|
||||||
bool negative_{false};
|
rep mantissa_{0};
|
||||||
internalrep mantissa_{0};
|
|
||||||
int exponent_{std::numeric_limits<int>::lowest()};
|
int exponent_{std::numeric_limits<int>::lowest()};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -217,10 +307,6 @@ public:
|
|||||||
constexpr static int minExponent = -32768;
|
constexpr static int minExponent = -32768;
|
||||||
constexpr static int maxExponent = 32768;
|
constexpr static int maxExponent = 32768;
|
||||||
|
|
||||||
constexpr static internalrep maxRep = std::numeric_limits<rep>::max();
|
|
||||||
static_assert(maxRep == 9'223'372'036'854'775'807);
|
|
||||||
static_assert(-maxRep == std::numeric_limits<rep>::min() + 1);
|
|
||||||
|
|
||||||
// May need to make unchecked private
|
// May need to make unchecked private
|
||||||
struct unchecked
|
struct unchecked
|
||||||
{
|
{
|
||||||
@@ -294,7 +380,7 @@ public:
|
|||||||
friend constexpr bool
|
friend constexpr bool
|
||||||
operator==(Number const& x, Number const& y) noexcept
|
operator==(Number const& x, Number const& y) noexcept
|
||||||
{
|
{
|
||||||
return x.negative_ == y.negative_ && x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend constexpr bool
|
friend constexpr bool
|
||||||
@@ -308,8 +394,8 @@ public:
|
|||||||
{
|
{
|
||||||
// If the two amounts have different signs (zero is treated as positive)
|
// If the two amounts have different signs (zero is treated as positive)
|
||||||
// then the comparison is true iff the left is negative.
|
// then the comparison is true iff the left is negative.
|
||||||
bool const lneg = x.negative_;
|
bool const lneg = x.mantissa_ < 0;
|
||||||
bool const rneg = y.negative_;
|
bool const rneg = y.mantissa_ < 0;
|
||||||
|
|
||||||
if (lneg != rneg)
|
if (lneg != rneg)
|
||||||
return lneg;
|
return lneg;
|
||||||
@@ -337,7 +423,7 @@ public:
|
|||||||
constexpr int
|
constexpr int
|
||||||
signum() const noexcept
|
signum() const noexcept
|
||||||
{
|
{
|
||||||
return negative_ ? -1 : (mantissa_ ? 1 : 0);
|
return mantissa_ < 0 ? -1 : (mantissa_ ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Number
|
Number
|
||||||
@@ -376,6 +462,9 @@ public:
|
|||||||
friend Number
|
friend Number
|
||||||
root2(Number f);
|
root2(Number f);
|
||||||
|
|
||||||
|
friend Number
|
||||||
|
power(Number const& f, unsigned n, unsigned d);
|
||||||
|
|
||||||
// Thread local rounding control. Default is to_nearest
|
// Thread local rounding control. Default is to_nearest
|
||||||
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||||
static rounding_mode
|
static rounding_mode
|
||||||
@@ -440,22 +529,39 @@ private:
|
|||||||
static_assert(isPowerOfTen(smallRange.min));
|
static_assert(isPowerOfTen(smallRange.min));
|
||||||
static_assert(smallRange.min == 1'000'000'000'000'000LL);
|
static_assert(smallRange.min == 1'000'000'000'000'000LL);
|
||||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||||
|
static_assert(smallRange.referenceMin == smallRange.min);
|
||||||
static_assert(smallRange.log == 15);
|
static_assert(smallRange.log == 15);
|
||||||
static_assert(smallRange.min < maxRep);
|
|
||||||
static_assert(smallRange.max < maxRep);
|
|
||||||
constexpr static MantissaRange largeRange{MantissaRange::large};
|
constexpr static MantissaRange largeRange{MantissaRange::large};
|
||||||
static_assert(isPowerOfTen(largeRange.min));
|
static_assert(!isPowerOfTen(largeRange.min));
|
||||||
static_assert(largeRange.min == 1'000'000'000'000'000'000ULL);
|
static_assert(largeRange.min == 922'337'203'685'477'581ULL);
|
||||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
static_assert(largeRange.max == internalrep(9'223'372'036'854'775'807ULL));
|
||||||
|
static_assert(largeRange.max == std::numeric_limits<rep>::max());
|
||||||
|
static_assert(largeRange.referenceMin == 1'000'000'000'000'000'000ULL);
|
||||||
static_assert(largeRange.log == 18);
|
static_assert(largeRange.log == 18);
|
||||||
static_assert(largeRange.min < maxRep);
|
// There are 2 values that will not fit in largeRange without some extra
|
||||||
static_assert(largeRange.max > maxRep);
|
// work
|
||||||
|
// * 9223372036854775808
|
||||||
|
// * 9223372036854775809
|
||||||
|
// They both end up < min, but with a leftover. If they round up, everything
|
||||||
|
// will be fine. If they don't, we'll need to bring them up into range.
|
||||||
|
// Guard::bringIntoRange handles this situation.
|
||||||
|
|
||||||
// The range for the mantissa when normalized.
|
// The range for the mantissa when normalized.
|
||||||
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
||||||
// changing the values inside the range.
|
// changing the values inside the range.
|
||||||
static thread_local std::reference_wrapper<MantissaRange const> range_;
|
static thread_local std::reference_wrapper<MantissaRange const> range_;
|
||||||
|
|
||||||
|
// And one is needed because it needs to choose between oneSmall and
|
||||||
|
// oneLarge based on the current range
|
||||||
|
static Number
|
||||||
|
one(MantissaRange const& range);
|
||||||
|
|
||||||
|
static Number
|
||||||
|
root(MantissaRange const& range, Number f, unsigned d);
|
||||||
|
|
||||||
|
void
|
||||||
|
normalize(MantissaRange const& range);
|
||||||
|
|
||||||
void
|
void
|
||||||
normalize();
|
normalize();
|
||||||
|
|
||||||
@@ -478,11 +584,14 @@ private:
|
|||||||
friend void
|
friend void
|
||||||
doNormalize(
|
doNormalize(
|
||||||
bool& negative,
|
bool& negative,
|
||||||
T& mantissa_,
|
T& mantissa,
|
||||||
int& exponent_,
|
int& exponent,
|
||||||
MantissaRange::rep const& minMantissa,
|
MantissaRange::rep const& minMantissa,
|
||||||
MantissaRange::rep const& maxMantissa);
|
MantissaRange::rep const& maxMantissa);
|
||||||
|
|
||||||
|
bool
|
||||||
|
isnormal(MantissaRange const& range) const noexcept;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isnormal() const noexcept;
|
isnormal() const noexcept;
|
||||||
|
|
||||||
@@ -492,18 +601,64 @@ private:
|
|||||||
Number
|
Number
|
||||||
shiftExponent(int exponentDelta) const;
|
shiftExponent(int exponentDelta) const;
|
||||||
|
|
||||||
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
// Safely return the absolute value of a rep (int64) mantissa as an internalrep (uint64).
|
||||||
// is negative, returns the positive value. This takes a little extra work
|
|
||||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
|
||||||
// UB, and can vary across compilers.
|
|
||||||
static internalrep
|
static internalrep
|
||||||
externalToInternal(rep mantissa);
|
externalToInternal(rep mantissa);
|
||||||
|
|
||||||
|
/** Breaks down the number into components, potentially de-normalizing it.
|
||||||
|
*
|
||||||
|
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <detail::UnsignedMantissa Rep = internalrep>
|
||||||
|
std::tuple<bool, Rep, int>
|
||||||
|
toInternal(MantissaRange const& range) const;
|
||||||
|
|
||||||
|
/** Breaks down the number into components, potentially de-normalizing it.
|
||||||
|
*
|
||||||
|
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <detail::UnsignedMantissa Rep = internalrep>
|
||||||
|
std::tuple<bool, Rep, int>
|
||||||
|
toInternal() const;
|
||||||
|
|
||||||
|
/** Rebuilds the number from components.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is true, the values are expected to be normalized - all
|
||||||
|
* in their valid ranges.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is false, the values are expected to be "near
|
||||||
|
* normalized", meaning that the mantissa has to be modified at most once to
|
||||||
|
* bring it back into range.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <bool expectNormal = true, detail::UnsignedMantissa Rep = internalrep>
|
||||||
|
void
|
||||||
|
fromInternal(bool negative, Rep mantissa, int exponent, MantissaRange const* pRange);
|
||||||
|
|
||||||
|
/** Rebuilds the number from components.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is true, the values are expected to be normalized - all
|
||||||
|
* in their valid ranges.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is false, the values are expected to be "near
|
||||||
|
* normalized", meaning that the mantissa has to be modified at most once to
|
||||||
|
* bring it back into range.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <bool expectNormal = true, detail::UnsignedMantissa Rep = internalrep>
|
||||||
|
void
|
||||||
|
fromInternal(bool negative, Rep mantissa, int exponent);
|
||||||
|
|
||||||
class Guard;
|
class Guard;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr static internalrep largestMantissa = largeRange.max;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline constexpr Number::Number(bool negative, internalrep mantissa, int exponent, unchecked) noexcept
|
inline constexpr Number::Number(bool negative, internalrep mantissa, int exponent, unchecked) noexcept
|
||||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
: mantissa_{negative ? -static_cast<rep>(mantissa) : static_cast<rep>(mantissa)}, exponent_{exponent}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,12 +669,6 @@ inline constexpr Number::Number(internalrep mantissa, int exponent, unchecked) n
|
|||||||
|
|
||||||
constexpr static Number numZero{};
|
constexpr static Number numZero{};
|
||||||
|
|
||||||
inline Number::Number(bool negative, internalrep mantissa, int exponent, normalized)
|
|
||||||
: Number(negative, mantissa, exponent, unchecked{})
|
|
||||||
{
|
|
||||||
normalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Number::Number(internalrep mantissa, int exponent, normalized) : Number(false, mantissa, exponent, normalized{})
|
inline Number::Number(internalrep mantissa, int exponent, normalized) : Number(false, mantissa, exponent, normalized{})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -541,17 +690,7 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
|||||||
inline constexpr Number::rep
|
inline constexpr Number::rep
|
||||||
Number::mantissa() const noexcept
|
Number::mantissa() const noexcept
|
||||||
{
|
{
|
||||||
auto m = mantissa_;
|
return mantissa_;
|
||||||
if (m > maxRep)
|
|
||||||
{
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
!isnormal() || (m % 10 == 0 && m / 10 <= maxRep),
|
|
||||||
"xrpl::Number::mantissa",
|
|
||||||
"large normalized mantissa has no remainder");
|
|
||||||
m /= 10;
|
|
||||||
}
|
|
||||||
auto const sign = negative_ ? -1 : 1;
|
|
||||||
return sign * static_cast<Number::rep>(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the exponent of the external view of the Number.
|
/** Returns the exponent of the external view of the Number.
|
||||||
@@ -562,16 +701,7 @@ Number::mantissa() const noexcept
|
|||||||
inline constexpr int
|
inline constexpr int
|
||||||
Number::exponent() const noexcept
|
Number::exponent() const noexcept
|
||||||
{
|
{
|
||||||
auto e = exponent_;
|
return exponent_;
|
||||||
if (mantissa_ > maxRep)
|
|
||||||
{
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
!isnormal() || (mantissa_ % 10 == 0 && mantissa_ / 10 <= maxRep),
|
|
||||||
"xrpl::Number::exponent",
|
|
||||||
"large normalized mantissa has no remainder");
|
|
||||||
++e;
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr Number
|
inline constexpr Number
|
||||||
@@ -586,7 +716,7 @@ Number::operator-() const noexcept
|
|||||||
if (mantissa_ == 0)
|
if (mantissa_ == 0)
|
||||||
return Number{};
|
return Number{};
|
||||||
auto x = *this;
|
auto x = *this;
|
||||||
x.negative_ = !x.negative_;
|
x.mantissa_ = -x.mantissa_;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,39 +797,55 @@ Number::min() noexcept
|
|||||||
inline Number
|
inline Number
|
||||||
Number::max() noexcept
|
Number::max() noexcept
|
||||||
{
|
{
|
||||||
return Number{false, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
return Number{false, range_.get().max, maxExponent, unchecked{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Number
|
inline Number
|
||||||
Number::lowest() noexcept
|
Number::lowest() noexcept
|
||||||
{
|
{
|
||||||
return Number{true, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
return Number{true, range_.get().max, maxExponent, unchecked{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
Number::isnormal(MantissaRange const& range) const noexcept
|
||||||
|
{
|
||||||
|
auto const abs_m = externalToInternal(mantissa_);
|
||||||
|
|
||||||
|
return *this == Number{} ||
|
||||||
|
(range.min <= abs_m && abs_m <= range.max && //
|
||||||
|
minExponent <= exponent_ && exponent_ <= maxExponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
Number::isnormal() const noexcept
|
Number::isnormal() const noexcept
|
||||||
{
|
{
|
||||||
MantissaRange const& range = range_;
|
return isnormal(range_);
|
||||||
auto const abs_m = mantissa_;
|
|
||||||
return *this == Number{} ||
|
|
||||||
(range.min <= abs_m && abs_m <= range.max && (abs_m <= maxRep || abs_m % 10 == 0) && minExponent <= exponent_ &&
|
|
||||||
exponent_ <= maxExponent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Integral64 T>
|
template <Integral64 T>
|
||||||
std::pair<T, int>
|
std::pair<T, int>
|
||||||
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
||||||
{
|
{
|
||||||
bool negative = negative_;
|
bool negative = mantissa_ < 0;
|
||||||
internalrep mantissa = mantissa_;
|
internalrep mantissa = externalToInternal(mantissa_);
|
||||||
int exponent = exponent_;
|
int exponent = exponent_;
|
||||||
|
|
||||||
if constexpr (std::is_unsigned_v<T>)
|
if constexpr (std::is_unsigned_v<T>)
|
||||||
|
{
|
||||||
XRPL_ASSERT_PARTS(!negative, "xrpl::Number::normalizeToRange", "Number is non-negative for unsigned range.");
|
XRPL_ASSERT_PARTS(!negative, "xrpl::Number::normalizeToRange", "Number is non-negative for unsigned range.");
|
||||||
|
// To avoid logical errors in release builds, throw if the Number is
|
||||||
|
// negative for an unsigned range.
|
||||||
|
if (negative)
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Number::normalizeToRange: Number is negative for "
|
||||||
|
"unsigned range.");
|
||||||
|
}
|
||||||
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||||
|
|
||||||
auto const sign = negative ? -1 : 1;
|
// Cast mantissa to signed type first (if T is a signed type) to avoid
|
||||||
return std::make_pair(static_cast<T>(sign * mantissa), exponent);
|
// unsigned integer overflow when multiplying by negative sign
|
||||||
|
T signedMantissa = negative ? -static_cast<T>(mantissa) : static_cast<T>(mantissa);
|
||||||
|
return std::make_pair(signedMantissa, exponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr Number
|
inline constexpr Number
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ std::size_t constexpr maxMPTokenMetadataLength = 1024;
|
|||||||
|
|
||||||
/** The maximum amount of MPTokenIssuance */
|
/** The maximum amount of MPTokenIssuance */
|
||||||
std::uint64_t constexpr maxMPTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull;
|
std::uint64_t constexpr maxMPTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull;
|
||||||
static_assert(Number::maxRep >= maxMPTokenAmount);
|
static_assert(Number::largestMantissa >= maxMPTokenAmount);
|
||||||
|
|
||||||
/** The maximum length of Data payload */
|
/** The maximum length of Data payload */
|
||||||
std::size_t constexpr maxDataPayloadLength = 256;
|
std::size_t constexpr maxDataPayloadLength = 256;
|
||||||
|
|||||||
@@ -521,6 +521,7 @@ STAmount::fromNumber(A const& a, Number const& number)
|
|||||||
return STAmount{asset, intValue, 0, negative};
|
return STAmount{asset, intValue, 0, negative};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XRPL_ASSERT_PARTS(working.signum() >= 0, "xrpl::STAmount::fromNumber", "non-negative Number to normalize");
|
||||||
auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue);
|
auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue);
|
||||||
|
|
||||||
return STAmount{asset, mantissa, exponent, negative};
|
return STAmount{asset, mantissa, exponent, negative};
|
||||||
|
|||||||
@@ -23,7 +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);
|
static_assert(INITIAL_XRP.drops() == 100'000'000'000'000'000);
|
||||||
static_assert(Number::maxRep >= INITIAL_XRP.drops());
|
static_assert(Number::largestMantissa >= INITIAL_XRP.drops());
|
||||||
|
|
||||||
/** 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
|
||||||
|
|||||||
@@ -11,18 +11,16 @@
|
|||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma message("Using boost::multiprecision::uint128_t and int128_t")
|
#pragma message("Using boost::multiprecision::uint128_t and int128_t")
|
||||||
#include <boost/multiprecision/cpp_int.hpp>
|
#endif
|
||||||
using uint128_t = boost::multiprecision::uint128_t;
|
|
||||||
using int128_t = boost::multiprecision::int128_t;
|
using uint128_t = xrpl::detail::uint128_t;
|
||||||
#else // !defined(_MSC_VER)
|
using int128_t = xrpl::detail::int128_t;
|
||||||
using uint128_t = __uint128_t;
|
|
||||||
using int128_t = __int128_t;
|
|
||||||
#endif // !defined(_MSC_VER)
|
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -61,9 +59,6 @@ Number::setMantissaScale(MantissaRange::mantissa_scale scale)
|
|||||||
// precision to an operation. This enables the final result
|
// precision to an operation. This enables the final result
|
||||||
// to be correctly rounded to the internal precision of Number.
|
// to be correctly rounded to the internal precision of Number.
|
||||||
|
|
||||||
template <class T>
|
|
||||||
concept UnsignedMantissa = std::is_unsigned_v<T> || std::is_same_v<T, uint128_t>;
|
|
||||||
|
|
||||||
class Number::Guard
|
class Number::Guard
|
||||||
{
|
{
|
||||||
std::uint64_t digits_; // 16 decimal guard digits
|
std::uint64_t digits_; // 16 decimal guard digits
|
||||||
@@ -99,7 +94,7 @@ public:
|
|||||||
round() noexcept;
|
round() noexcept;
|
||||||
|
|
||||||
// Modify the result to the correctly rounded value
|
// Modify the result to the correctly rounded value
|
||||||
template <UnsignedMantissa T>
|
template <detail::UnsignedMantissa T>
|
||||||
void
|
void
|
||||||
doRoundUp(
|
doRoundUp(
|
||||||
bool& negative,
|
bool& negative,
|
||||||
@@ -107,22 +102,22 @@ public:
|
|||||||
int& exponent,
|
int& exponent,
|
||||||
internalrep const& minMantissa,
|
internalrep const& minMantissa,
|
||||||
internalrep const& maxMantissa,
|
internalrep const& maxMantissa,
|
||||||
std::string location);
|
std::string_view location);
|
||||||
|
|
||||||
// Modify the result to the correctly rounded value
|
// Modify the result to the correctly rounded value
|
||||||
template <UnsignedMantissa T>
|
template <detail::UnsignedMantissa T>
|
||||||
void
|
void
|
||||||
doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
||||||
|
|
||||||
// Modify the result to the correctly rounded value
|
// Modify the result to the correctly rounded value
|
||||||
void
|
void
|
||||||
doRound(rep& drops, std::string location);
|
doRound(rep& drops, std::string_view location);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void
|
void
|
||||||
doPush(unsigned d) noexcept;
|
doPush(unsigned d) noexcept;
|
||||||
|
|
||||||
template <UnsignedMantissa T>
|
template <detail::UnsignedMantissa T>
|
||||||
void
|
void
|
||||||
bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
||||||
};
|
};
|
||||||
@@ -209,7 +204,7 @@ Number::Guard::round() noexcept
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <UnsignedMantissa T>
|
template <detail::UnsignedMantissa T>
|
||||||
void
|
void
|
||||||
Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa)
|
Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa)
|
||||||
{
|
{
|
||||||
@@ -224,13 +219,13 @@ Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent, intern
|
|||||||
{
|
{
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
|
|
||||||
negative = zero.negative_;
|
negative = false;
|
||||||
mantissa = zero.mantissa_;
|
mantissa = zero.mantissa_;
|
||||||
exponent = zero.exponent_;
|
exponent = zero.exponent_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <UnsignedMantissa T>
|
template <detail::UnsignedMantissa T>
|
||||||
void
|
void
|
||||||
Number::Guard::doRoundUp(
|
Number::Guard::doRoundUp(
|
||||||
bool& negative,
|
bool& negative,
|
||||||
@@ -238,7 +233,7 @@ Number::Guard::doRoundUp(
|
|||||||
int& exponent,
|
int& exponent,
|
||||||
internalrep const& minMantissa,
|
internalrep const& minMantissa,
|
||||||
internalrep const& maxMantissa,
|
internalrep const& maxMantissa,
|
||||||
std::string location)
|
std::string_view location)
|
||||||
{
|
{
|
||||||
auto r = round();
|
auto r = round();
|
||||||
if (r == 1 || (r == 0 && (mantissa & 1) == 1))
|
if (r == 1 || (r == 0 && (mantissa & 1) == 1))
|
||||||
@@ -246,7 +241,7 @@ Number::Guard::doRoundUp(
|
|||||||
++mantissa;
|
++mantissa;
|
||||||
// Ensure mantissa after incrementing fits within both the
|
// Ensure mantissa after incrementing fits within both the
|
||||||
// min/maxMantissa range and is a valid "rep".
|
// min/maxMantissa range and is a valid "rep".
|
||||||
if (mantissa > maxMantissa || mantissa > maxRep)
|
if (mantissa > maxMantissa)
|
||||||
{
|
{
|
||||||
mantissa /= 10;
|
mantissa /= 10;
|
||||||
++exponent;
|
++exponent;
|
||||||
@@ -254,10 +249,10 @@ Number::Guard::doRoundUp(
|
|||||||
}
|
}
|
||||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||||
if (exponent > maxExponent)
|
if (exponent > maxExponent)
|
||||||
throw std::overflow_error(location);
|
throw std::overflow_error(std::string{location});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <UnsignedMantissa T>
|
template <detail::UnsignedMantissa T>
|
||||||
void
|
void
|
||||||
Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa)
|
Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa)
|
||||||
{
|
{
|
||||||
@@ -276,21 +271,22 @@ Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent, internalr
|
|||||||
|
|
||||||
// Modify the result to the correctly rounded value
|
// Modify the result to the correctly rounded value
|
||||||
void
|
void
|
||||||
Number::Guard::doRound(rep& drops, std::string location)
|
Number::Guard::doRound(rep& drops, std::string_view location)
|
||||||
{
|
{
|
||||||
auto r = round();
|
auto r = round();
|
||||||
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
||||||
{
|
{
|
||||||
if (drops >= maxRep)
|
auto const& range = range_.get();
|
||||||
|
if (drops >= range.max)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(internalrep) == sizeof(rep));
|
static_assert(sizeof(internalrep) == sizeof(rep));
|
||||||
// This should be impossible, because it's impossible to represent
|
// This should be impossible, because it's impossible to represent
|
||||||
// "maxRep + 0.6" in Number, regardless of the scale. There aren't
|
// "largestMantissa + 0.6" in Number, regardless of the scale. There aren't
|
||||||
// enough digits available. You'd either get a mantissa of "maxRep"
|
// enough digits available. You'd either get a mantissa of "largestMantissa "
|
||||||
// or "(maxRep + 1) / 10", neither of which will round up when
|
// or "largestMantissa / 10 + 1", neither of which will round up when
|
||||||
// converting to rep, though the latter might overflow _before_
|
// converting to rep, though the latter might overflow _before_
|
||||||
// rounding.
|
// rounding.
|
||||||
throw std::overflow_error(location); // LCOV_EXCL_LINE
|
throw std::overflow_error(std::string{location}); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
++drops;
|
++drops;
|
||||||
}
|
}
|
||||||
@@ -310,23 +306,126 @@ Number::externalToInternal(rep mantissa)
|
|||||||
// If the mantissa is already positive, just return it
|
// If the mantissa is already positive, just return it
|
||||||
if (mantissa >= 0)
|
if (mantissa >= 0)
|
||||||
return mantissa;
|
return mantissa;
|
||||||
// If the mantissa is negative, but fits within the positive range of rep,
|
|
||||||
// return it negated
|
|
||||||
if (mantissa >= -std::numeric_limits<rep>::max())
|
|
||||||
return -mantissa;
|
|
||||||
|
|
||||||
// If the mantissa doesn't fit within the positive range, convert to
|
// Cast to unsigned before negating to avoid undefined behavior
|
||||||
// int128_t, negate that, and cast it back down to the internalrep
|
// when v == INT64_MIN (negating INT64_MIN in signed is UB)
|
||||||
// In practice, this is only going to cover the case of
|
return -static_cast<internalrep>(mantissa);
|
||||||
// std::numeric_limits<rep>::min().
|
}
|
||||||
int128_t temp = mantissa;
|
|
||||||
return static_cast<internalrep>(-temp);
|
/** Breaks down the number into components, potentially de-normalizing it.
|
||||||
|
*
|
||||||
|
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <detail::UnsignedMantissa Rep>
|
||||||
|
std::tuple<bool, Rep, int>
|
||||||
|
Number::toInternal(MantissaRange const& range) const
|
||||||
|
{
|
||||||
|
auto exponent = exponent_;
|
||||||
|
bool const negative = mantissa_ < 0;
|
||||||
|
// It should be impossible for mantissa_ to be INT64_MIN, but use externalToInternal just in case.
|
||||||
|
Rep mantissa = static_cast<Rep>(externalToInternal(mantissa_));
|
||||||
|
|
||||||
|
auto const referenceMin = range.referenceMin;
|
||||||
|
auto const minMantissa = range.min;
|
||||||
|
|
||||||
|
if (mantissa != 0 && mantissa >= minMantissa && mantissa < referenceMin)
|
||||||
|
{
|
||||||
|
// Ensure the mantissa has the correct number of digits
|
||||||
|
mantissa *= 10;
|
||||||
|
--exponent;
|
||||||
|
XRPL_ASSERT_PARTS(
|
||||||
|
mantissa >= referenceMin && mantissa < referenceMin * 10,
|
||||||
|
"xrpl::Number::toInternal()",
|
||||||
|
"Number is within reference range and has 'log' digits");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {negative, mantissa, exponent};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Breaks down the number into components, potentially de-normalizing it.
|
||||||
|
*
|
||||||
|
* Ensures that the mantissa always has exactly range_.log + 1 digits.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <detail::UnsignedMantissa Rep>
|
||||||
|
std::tuple<bool, Rep, int>
|
||||||
|
Number::toInternal() const
|
||||||
|
{
|
||||||
|
return toInternal(range_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rebuilds the number from components.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is true, the values are expected to be normalized - all
|
||||||
|
* in their valid ranges.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is false, the values are expected to be "near
|
||||||
|
* normalized", meaning that the mantissa has to be modified at most once to
|
||||||
|
* bring it back into range.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <bool expectNormal, detail::UnsignedMantissa Rep>
|
||||||
|
void
|
||||||
|
Number::fromInternal(bool negative, Rep mantissa, int exponent, MantissaRange const* pRange)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<std::bool_constant<expectNormal>, std::false_type>)
|
||||||
|
{
|
||||||
|
if (!pRange)
|
||||||
|
throw std::runtime_error("Missing range to Number::fromInternal!");
|
||||||
|
auto const& range = *pRange;
|
||||||
|
|
||||||
|
auto const maxMantissa = range.max;
|
||||||
|
auto const minMantissa = range.min;
|
||||||
|
|
||||||
|
XRPL_ASSERT_PARTS(mantissa >= minMantissa, "xrpl::Number::fromInternal", "mantissa large enough");
|
||||||
|
|
||||||
|
if (mantissa > maxMantissa || mantissa < minMantissa)
|
||||||
|
{
|
||||||
|
normalize(negative, mantissa, exponent, range.min, maxMantissa);
|
||||||
|
}
|
||||||
|
|
||||||
|
XRPL_ASSERT_PARTS(
|
||||||
|
mantissa >= minMantissa && mantissa <= maxMantissa, "xrpl::Number::fromInternal", "mantissa in range");
|
||||||
|
}
|
||||||
|
|
||||||
|
// mantissa is unsigned, but it might not be uint64
|
||||||
|
mantissa_ = static_cast<rep>(static_cast<internalrep>(mantissa));
|
||||||
|
if (negative)
|
||||||
|
mantissa_ = -mantissa_;
|
||||||
|
exponent_ = exponent;
|
||||||
|
|
||||||
|
XRPL_ASSERT_PARTS(
|
||||||
|
(pRange && isnormal(*pRange)) || isnormal(), "xrpl::Number::fromInternal", "Number is normalized");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rebuilds the number from components.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is true, the values are expected to be normalized - all in
|
||||||
|
* their valid ranges.
|
||||||
|
*
|
||||||
|
* If "expectNormal" is false, the values are expected to be "near normalized",
|
||||||
|
* meaning that the mantissa has to be modified at most once to bring it back
|
||||||
|
* into range.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <bool expectNormal, detail::UnsignedMantissa Rep>
|
||||||
|
void
|
||||||
|
Number::fromInternal(bool negative, Rep mantissa, int exponent)
|
||||||
|
{
|
||||||
|
MantissaRange const* pRange = nullptr;
|
||||||
|
if constexpr (std::is_same_v<std::bool_constant<expectNormal>, std::false_type>)
|
||||||
|
{
|
||||||
|
pRange = &Number::range_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
fromInternal(negative, mantissa, exponent, pRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr Number
|
constexpr Number
|
||||||
Number::oneSmall()
|
Number::oneSmall()
|
||||||
{
|
{
|
||||||
return Number{false, Number::smallRange.min, -Number::smallRange.log, Number::unchecked{}};
|
return Number{false, Number::smallRange.referenceMin, -Number::smallRange.log, Number::unchecked{}};
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Number oneSml = Number::oneSmall();
|
constexpr Number oneSml = Number::oneSmall();
|
||||||
@@ -334,101 +433,84 @@ constexpr Number oneSml = Number::oneSmall();
|
|||||||
constexpr Number
|
constexpr Number
|
||||||
Number::oneLarge()
|
Number::oneLarge()
|
||||||
{
|
{
|
||||||
return Number{false, Number::largeRange.min, -Number::largeRange.log, Number::unchecked{}};
|
return Number{false, Number::largeRange.referenceMin, -Number::largeRange.log, Number::unchecked{}};
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Number oneLrg = Number::oneLarge();
|
constexpr Number oneLrg = Number::oneLarge();
|
||||||
|
|
||||||
Number
|
Number
|
||||||
Number::one()
|
Number::one(MantissaRange const& range)
|
||||||
{
|
{
|
||||||
if (&range_.get() == &smallRange)
|
if (&range == &smallRange)
|
||||||
return oneSml;
|
return oneSml;
|
||||||
XRPL_ASSERT(&range_.get() == &largeRange, "Number::one() : valid range_");
|
XRPL_ASSERT(&range == &largeRange, "Number::one() : valid range");
|
||||||
return oneLrg;
|
return oneLrg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Number
|
||||||
|
Number::one()
|
||||||
|
{
|
||||||
|
return one(range_);
|
||||||
|
}
|
||||||
|
|
||||||
// Use the member names in this static function for now so the diff is cleaner
|
// Use the member names in this static function for now so the diff is cleaner
|
||||||
// TODO: Rename the function parameters to get rid of the "_" suffix
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void
|
void
|
||||||
doNormalize(
|
doNormalize(
|
||||||
bool& negative,
|
bool& negative,
|
||||||
T& mantissa_,
|
T& mantissa,
|
||||||
int& exponent_,
|
int& exponent,
|
||||||
MantissaRange::rep const& minMantissa,
|
MantissaRange::rep const& minMantissa,
|
||||||
MantissaRange::rep const& maxMantissa)
|
MantissaRange::rep const& maxMantissa)
|
||||||
{
|
{
|
||||||
auto constexpr minExponent = Number::minExponent;
|
auto constexpr minExponent = Number::minExponent;
|
||||||
auto constexpr maxExponent = Number::maxExponent;
|
auto constexpr maxExponent = Number::maxExponent;
|
||||||
auto constexpr maxRep = Number::maxRep;
|
|
||||||
|
|
||||||
using Guard = Number::Guard;
|
using Guard = Number::Guard;
|
||||||
|
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
if (mantissa_ == 0)
|
if (mantissa == 0 || (mantissa < minMantissa && exponent <= minExponent))
|
||||||
{
|
{
|
||||||
mantissa_ = zero.mantissa_;
|
mantissa = zero.mantissa_;
|
||||||
exponent_ = zero.exponent_;
|
exponent = zero.exponent_;
|
||||||
negative = zero.negative_;
|
negative = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto m = mantissa_;
|
|
||||||
while ((m < minMantissa) && (exponent_ > minExponent))
|
auto m = mantissa;
|
||||||
|
while ((m < minMantissa) && (exponent > minExponent))
|
||||||
{
|
{
|
||||||
m *= 10;
|
m *= 10;
|
||||||
--exponent_;
|
--exponent;
|
||||||
}
|
}
|
||||||
Guard g;
|
Guard g;
|
||||||
if (negative)
|
if (negative)
|
||||||
g.set_negative();
|
g.set_negative();
|
||||||
while (m > maxMantissa)
|
while (m > maxMantissa)
|
||||||
{
|
{
|
||||||
if (exponent_ >= maxExponent)
|
if (exponent >= maxExponent)
|
||||||
throw std::overflow_error("Number::normalize 1");
|
throw std::overflow_error("Number::normalize 1");
|
||||||
g.push(m % 10);
|
g.push(m % 10);
|
||||||
m /= 10;
|
m /= 10;
|
||||||
++exponent_;
|
++exponent;
|
||||||
}
|
}
|
||||||
if ((exponent_ < minExponent) || (m < minMantissa))
|
if ((exponent < minExponent) || (m == 0))
|
||||||
{
|
{
|
||||||
mantissa_ = zero.mantissa_;
|
mantissa = zero.mantissa_;
|
||||||
exponent_ = zero.exponent_;
|
exponent = zero.exponent_;
|
||||||
negative = zero.negative_;
|
negative = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When using the largeRange, "m" needs fit within an int64, even if
|
XRPL_ASSERT_PARTS(m <= maxMantissa, "xrpl::doNormalize", "intermediate mantissa fits in int64");
|
||||||
// the final mantissa_ is going to end up larger to fit within the
|
mantissa = m;
|
||||||
// MantissaRange. Cut it down here so that the rounding will be done while
|
|
||||||
// it's smaller.
|
g.doRoundUp(negative, mantissa, exponent, minMantissa, maxMantissa, "Number::normalize 2");
|
||||||
//
|
|
||||||
// Example: 9,900,000,000,000,123,456 > 9,223,372,036,854,775,807,
|
|
||||||
// so "m" will be modified to 990,000,000,000,012,345. Then that value
|
|
||||||
// will be rounded to 990,000,000,000,012,345 or
|
|
||||||
// 990,000,000,000,012,346, depending on the rounding mode. Finally,
|
|
||||||
// mantissa_ will be "m*10" so it fits within the range, and end up as
|
|
||||||
// 9,900,000,000,000,123,450 or 9,900,000,000,000,123,460.
|
|
||||||
// mantissa() will return mantissa_ / 10, and exponent() will return
|
|
||||||
// exponent_ + 1.
|
|
||||||
if (m > maxRep)
|
|
||||||
{
|
|
||||||
if (exponent_ >= maxExponent)
|
|
||||||
throw std::overflow_error("Number::normalize 1.5");
|
|
||||||
g.push(m % 10);
|
|
||||||
m /= 10;
|
|
||||||
++exponent_;
|
|
||||||
}
|
|
||||||
// Before modification, m should be within the min/max range. After
|
|
||||||
// modification, it must be less than maxRep. In other words, the original
|
|
||||||
// value should have been no more than maxRep * 10.
|
|
||||||
// (maxRep * 10 > maxMantissa)
|
|
||||||
XRPL_ASSERT_PARTS(m <= maxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64");
|
|
||||||
mantissa_ = m;
|
|
||||||
|
|
||||||
g.doRoundUp(negative, mantissa_, exponent_, minMantissa, maxMantissa, "Number::normalize 2");
|
|
||||||
XRPL_ASSERT_PARTS(
|
XRPL_ASSERT_PARTS(
|
||||||
mantissa_ >= minMantissa && mantissa_ <= maxMantissa, "xrpl::doNormalize", "final mantissa fits in range");
|
mantissa >= minMantissa && mantissa <= maxMantissa, "xrpl::doNormalize", "final mantissa fits in range");
|
||||||
|
XRPL_ASSERT_PARTS(
|
||||||
|
exponent >= minExponent && exponent <= maxExponent, "xrpl::doNormalize", "final exponent fits in range");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -467,11 +549,20 @@ Number::normalize<unsigned long>(
|
|||||||
doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Number::normalize(MantissaRange const& range)
|
||||||
|
{
|
||||||
|
auto [negative, mantissa, exponent] = toInternal(range);
|
||||||
|
|
||||||
|
normalize(negative, mantissa, exponent, range.min, range.max);
|
||||||
|
|
||||||
|
fromInternal(negative, mantissa, exponent, &range);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Number::normalize()
|
Number::normalize()
|
||||||
{
|
{
|
||||||
auto const& range = range_.get();
|
normalize(range_);
|
||||||
normalize(negative_, mantissa_, exponent_, range.min, range.max);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the number, but set a new exponent. Because the mantissa doesn't change,
|
// Copy the number, but set a new exponent. Because the mantissa doesn't change,
|
||||||
@@ -481,21 +572,33 @@ Number
|
|||||||
Number::shiftExponent(int exponentDelta) const
|
Number::shiftExponent(int exponentDelta) const
|
||||||
{
|
{
|
||||||
XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::shiftExponent", "normalized");
|
XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::shiftExponent", "normalized");
|
||||||
auto const newExponent = exponent_ + exponentDelta;
|
|
||||||
if (newExponent >= maxExponent)
|
Number result = *this;
|
||||||
|
|
||||||
|
result.exponent_ += exponentDelta;
|
||||||
|
|
||||||
|
if (result.exponent_ >= maxExponent)
|
||||||
throw std::overflow_error("Number::shiftExponent");
|
throw std::overflow_error("Number::shiftExponent");
|
||||||
if (newExponent < minExponent)
|
if (result.exponent_ < minExponent)
|
||||||
{
|
{
|
||||||
return Number{};
|
return Number{};
|
||||||
}
|
}
|
||||||
Number const result{negative_, mantissa_, newExponent, unchecked{}};
|
|
||||||
XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::Number::shiftExponent", "result is normalized");
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Number::Number(bool negative, internalrep mantissa, int exponent, normalized)
|
||||||
|
{
|
||||||
|
auto const& range = range_.get();
|
||||||
|
normalize(negative, mantissa, exponent, range.min, range.max);
|
||||||
|
fromInternal(negative, mantissa, exponent, &range);
|
||||||
|
}
|
||||||
|
|
||||||
Number&
|
Number&
|
||||||
Number::operator+=(Number const& y)
|
Number::operator+=(Number const& y)
|
||||||
{
|
{
|
||||||
|
auto const& range = range_.get();
|
||||||
|
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
if (y == zero)
|
if (y == zero)
|
||||||
return *this;
|
return *this;
|
||||||
@@ -510,7 +613,7 @@ Number::operator+=(Number const& y)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
XRPL_ASSERT(isnormal() && y.isnormal(), "xrpl::Number::operator+=(Number) : is normal");
|
XRPL_ASSERT(isnormal(range) && y.isnormal(range), "xrpl::Number::operator+=(Number) : is normal");
|
||||||
// *n = negative
|
// *n = negative
|
||||||
// *s = sign
|
// *s = sign
|
||||||
// *m = mantissa
|
// *m = mantissa
|
||||||
@@ -518,13 +621,10 @@ Number::operator+=(Number const& y)
|
|||||||
|
|
||||||
// Need to use uint128_t, because large mantissas can overflow when added
|
// Need to use uint128_t, because large mantissas can overflow when added
|
||||||
// together.
|
// together.
|
||||||
bool xn = negative_;
|
auto [xn, xm, xe] = toInternal<uint128_t>(range);
|
||||||
uint128_t xm = mantissa_;
|
|
||||||
auto xe = exponent_;
|
auto [yn, ym, ye] = y.toInternal<uint128_t>(range);
|
||||||
|
|
||||||
bool yn = y.negative_;
|
|
||||||
uint128_t ym = y.mantissa_;
|
|
||||||
auto ye = y.exponent_;
|
|
||||||
Guard g;
|
Guard g;
|
||||||
if (xe < ye)
|
if (xe < ye)
|
||||||
{
|
{
|
||||||
@@ -549,14 +649,13 @@ Number::operator+=(Number const& y)
|
|||||||
} while (xe > ye);
|
} while (xe > ye);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const& range = range_.get();
|
|
||||||
auto const& minMantissa = range.min;
|
auto const& minMantissa = range.min;
|
||||||
auto const& maxMantissa = range.max;
|
auto const& maxMantissa = range.max;
|
||||||
|
|
||||||
if (xn == yn)
|
if (xn == yn)
|
||||||
{
|
{
|
||||||
xm += ym;
|
xm += ym;
|
||||||
if (xm > maxMantissa || xm > maxRep)
|
if (xm > maxMantissa)
|
||||||
{
|
{
|
||||||
g.push(xm % 10);
|
g.push(xm % 10);
|
||||||
xm /= 10;
|
xm /= 10;
|
||||||
@@ -576,7 +675,7 @@ Number::operator+=(Number const& y)
|
|||||||
xe = ye;
|
xe = ye;
|
||||||
xn = yn;
|
xn = yn;
|
||||||
}
|
}
|
||||||
while (xm < minMantissa && xm * 10 <= maxRep)
|
while (xm < minMantissa)
|
||||||
{
|
{
|
||||||
xm *= 10;
|
xm *= 10;
|
||||||
xm -= g.pop();
|
xm -= g.pop();
|
||||||
@@ -585,10 +684,8 @@ Number::operator+=(Number const& y)
|
|||||||
g.doRoundDown(xn, xm, xe, minMantissa);
|
g.doRoundDown(xn, xm, xe, minMantissa);
|
||||||
}
|
}
|
||||||
|
|
||||||
negative_ = xn;
|
normalize(xn, xm, xe, minMantissa, maxMantissa);
|
||||||
mantissa_ = static_cast<internalrep>(xm);
|
fromInternal(xn, xm, xe, &range);
|
||||||
exponent_ = xe;
|
|
||||||
normalize();
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,6 +720,8 @@ divu10(uint128_t& u)
|
|||||||
Number&
|
Number&
|
||||||
Number::operator*=(Number const& y)
|
Number::operator*=(Number const& y)
|
||||||
{
|
{
|
||||||
|
auto const& range = range_.get();
|
||||||
|
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
if (*this == zero)
|
if (*this == zero)
|
||||||
return *this;
|
return *this;
|
||||||
@@ -636,15 +735,11 @@ Number::operator*=(Number const& y)
|
|||||||
// *m = mantissa
|
// *m = mantissa
|
||||||
// *e = exponent
|
// *e = exponent
|
||||||
|
|
||||||
bool xn = negative_;
|
auto [xn, xm, xe] = toInternal(range);
|
||||||
int xs = xn ? -1 : 1;
|
int xs = xn ? -1 : 1;
|
||||||
internalrep xm = mantissa_;
|
|
||||||
auto xe = exponent_;
|
|
||||||
|
|
||||||
bool yn = y.negative_;
|
auto [yn, ym, ye] = y.toInternal(range);
|
||||||
int ys = yn ? -1 : 1;
|
int ys = yn ? -1 : 1;
|
||||||
internalrep ym = y.mantissa_;
|
|
||||||
auto ye = y.exponent_;
|
|
||||||
|
|
||||||
auto zm = uint128_t(xm) * uint128_t(ym);
|
auto zm = uint128_t(xm) * uint128_t(ym);
|
||||||
auto ze = xe + ye;
|
auto ze = xe + ye;
|
||||||
@@ -654,11 +749,10 @@ Number::operator*=(Number const& y)
|
|||||||
if (zn)
|
if (zn)
|
||||||
g.set_negative();
|
g.set_negative();
|
||||||
|
|
||||||
auto const& range = range_.get();
|
|
||||||
auto const& minMantissa = range.min;
|
auto const& minMantissa = range.min;
|
||||||
auto const& maxMantissa = range.max;
|
auto const& maxMantissa = range.max;
|
||||||
|
|
||||||
while (zm > maxMantissa || zm > maxRep)
|
while (zm > maxMantissa)
|
||||||
{
|
{
|
||||||
// The following is optimization for:
|
// The following is optimization for:
|
||||||
// g.push(static_cast<unsigned>(zm % 10));
|
// g.push(static_cast<unsigned>(zm % 10));
|
||||||
@@ -670,17 +764,17 @@ Number::operator*=(Number const& y)
|
|||||||
xe = ze;
|
xe = ze;
|
||||||
g.doRoundUp(
|
g.doRoundUp(
|
||||||
zn, xm, xe, minMantissa, maxMantissa, "Number::multiplication overflow : exponent is " + std::to_string(xe));
|
zn, xm, xe, minMantissa, maxMantissa, "Number::multiplication overflow : exponent is " + std::to_string(xe));
|
||||||
negative_ = zn;
|
|
||||||
mantissa_ = xm;
|
|
||||||
exponent_ = xe;
|
|
||||||
|
|
||||||
normalize();
|
normalize(zn, xm, xe, minMantissa, maxMantissa);
|
||||||
|
fromInternal(zn, xm, xe, &range);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Number&
|
Number&
|
||||||
Number::operator/=(Number const& y)
|
Number::operator/=(Number const& y)
|
||||||
{
|
{
|
||||||
|
auto const& range = range_.get();
|
||||||
|
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
if (y == zero)
|
if (y == zero)
|
||||||
throw std::overflow_error("Number: divide by 0");
|
throw std::overflow_error("Number: divide by 0");
|
||||||
@@ -693,17 +787,12 @@ Number::operator/=(Number const& y)
|
|||||||
// *m = mantissa
|
// *m = mantissa
|
||||||
// *e = exponent
|
// *e = exponent
|
||||||
|
|
||||||
bool np = negative_;
|
auto [np, nm, ne] = toInternal(range);
|
||||||
int ns = (np ? -1 : 1);
|
int ns = (np ? -1 : 1);
|
||||||
auto nm = mantissa_;
|
|
||||||
auto ne = exponent_;
|
|
||||||
|
|
||||||
bool dp = y.negative_;
|
auto [dp, dm, de] = y.toInternal(range);
|
||||||
int ds = (dp ? -1 : 1);
|
int ds = (dp ? -1 : 1);
|
||||||
auto dm = y.mantissa_;
|
|
||||||
auto de = y.exponent_;
|
|
||||||
|
|
||||||
auto const& range = range_.get();
|
|
||||||
auto const& minMantissa = range.min;
|
auto const& minMantissa = range.min;
|
||||||
auto const& maxMantissa = range.max;
|
auto const& maxMantissa = range.max;
|
||||||
|
|
||||||
@@ -715,7 +804,7 @@ Number::operator/=(Number const& y)
|
|||||||
// f can be up to 10^(38-19) = 10^19 safely
|
// f can be up to 10^(38-19) = 10^19 safely
|
||||||
static_assert(smallRange.log == 15);
|
static_assert(smallRange.log == 15);
|
||||||
static_assert(largeRange.log == 18);
|
static_assert(largeRange.log == 18);
|
||||||
bool small = Number::getMantissaScale() == MantissaRange::small;
|
bool small = range.scale == MantissaRange::small;
|
||||||
uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL;
|
uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL;
|
||||||
XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size");
|
XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size");
|
||||||
|
|
||||||
@@ -765,10 +854,8 @@ Number::operator/=(Number const& y)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
normalize(zn, zm, ze, minMantissa, maxMantissa);
|
normalize(zn, zm, ze, minMantissa, maxMantissa);
|
||||||
negative_ = zn;
|
fromInternal(zn, zm, ze, &range);
|
||||||
mantissa_ = static_cast<internalrep>(zm);
|
XRPL_ASSERT_PARTS(isnormal(range), "xrpl::Number::operator/=", "result is normalized");
|
||||||
exponent_ = ze;
|
|
||||||
XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::operator/=", "result is normalized");
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -780,10 +867,10 @@ Number::operator rep() const
|
|||||||
Guard g;
|
Guard g;
|
||||||
if (drops != 0)
|
if (drops != 0)
|
||||||
{
|
{
|
||||||
if (negative_)
|
if (drops < 0)
|
||||||
{
|
{
|
||||||
g.set_negative();
|
g.set_negative();
|
||||||
drops = -drops;
|
drops = externalToInternal(drops);
|
||||||
}
|
}
|
||||||
for (; offset < 0; ++offset)
|
for (; offset < 0; ++offset)
|
||||||
{
|
{
|
||||||
@@ -792,7 +879,7 @@ Number::operator rep() const
|
|||||||
}
|
}
|
||||||
for (; offset > 0; --offset)
|
for (; offset > 0; --offset)
|
||||||
{
|
{
|
||||||
if (drops > maxRep / 10)
|
if (drops >= largeRange.min)
|
||||||
throw std::overflow_error("Number::operator rep() overflow");
|
throw std::overflow_error("Number::operator rep() overflow");
|
||||||
drops *= 10;
|
drops *= 10;
|
||||||
}
|
}
|
||||||
@@ -822,19 +909,21 @@ Number::truncate() const noexcept
|
|||||||
std::string
|
std::string
|
||||||
to_string(Number const& amount)
|
to_string(Number const& amount)
|
||||||
{
|
{
|
||||||
|
auto const& range = Number::range_.get();
|
||||||
|
|
||||||
// keep full internal accuracy, but make more human friendly if possible
|
// keep full internal accuracy, but make more human friendly if possible
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
if (amount == zero)
|
if (amount == zero)
|
||||||
return "0";
|
return "0";
|
||||||
|
|
||||||
auto exponent = amount.exponent_;
|
// The mantissa must have a set number of decimal places for this to work
|
||||||
auto mantissa = amount.mantissa_;
|
auto [negative, mantissa, exponent] = amount.toInternal(range);
|
||||||
bool const negative = amount.negative_;
|
|
||||||
|
|
||||||
// Use scientific notation for exponents that are too small or too large
|
// Use scientific notation for exponents that are too small or too large
|
||||||
auto const rangeLog = Number::mantissaLog();
|
auto const rangeLog = range.log;
|
||||||
if (((exponent != 0) && ((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
|
if (((exponent != 0 && amount.exponent() != 0) && ((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
|
||||||
{
|
{
|
||||||
|
// Remove trailing zeroes from the mantissa.
|
||||||
while (mantissa != 0 && mantissa % 10 == 0 && exponent < Number::maxExponent)
|
while (mantissa != 0 && mantissa % 10 == 0 && exponent < Number::maxExponent)
|
||||||
{
|
{
|
||||||
mantissa /= 10;
|
mantissa /= 10;
|
||||||
@@ -842,8 +931,11 @@ to_string(Number const& amount)
|
|||||||
}
|
}
|
||||||
std::string ret = negative ? "-" : "";
|
std::string ret = negative ? "-" : "";
|
||||||
ret.append(std::to_string(mantissa));
|
ret.append(std::to_string(mantissa));
|
||||||
ret.append(1, 'e');
|
if (exponent != 0)
|
||||||
ret.append(std::to_string(exponent));
|
{
|
||||||
|
ret.append(1, 'e');
|
||||||
|
ret.append(std::to_string(exponent));
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,20 +1017,11 @@ power(Number const& f, unsigned n)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns f^(1/d)
|
|
||||||
// Uses Newton–Raphson iterations until the result stops changing
|
|
||||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
|
||||||
|
|
||||||
// This function, and power(Number f, unsigned n, unsigned d)
|
|
||||||
// treat corner cases such as 0 roots as advised by Annex F of
|
|
||||||
// the C standard, which itself is consistent with the IEEE
|
|
||||||
// floating point standards.
|
|
||||||
|
|
||||||
Number
|
Number
|
||||||
root(Number f, unsigned d)
|
Number::root(MantissaRange const& range, Number f, unsigned d)
|
||||||
{
|
{
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
auto const one = Number::one();
|
auto const one = Number::one(range);
|
||||||
|
|
||||||
if (f == one || d == 1)
|
if (f == one || d == 1)
|
||||||
return f;
|
return f;
|
||||||
@@ -955,21 +1038,28 @@ root(Number f, unsigned d)
|
|||||||
if (f == zero)
|
if (f == zero)
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
auto const [e, di] = [&]() {
|
||||||
auto e = f.exponent_ + Number::mantissaLog() + 1;
|
auto const [negative, mantissa, exponent] = f.toInternal(range);
|
||||||
auto const di = static_cast<int>(d);
|
|
||||||
auto ex = [e = e, di = di]() // Euclidean remainder of e/d
|
|
||||||
{
|
|
||||||
int k = (e >= 0 ? e : e - (di - 1)) / di;
|
|
||||||
int k2 = e - k * di;
|
|
||||||
if (k2 == 0)
|
|
||||||
return 0;
|
|
||||||
return di - k2;
|
|
||||||
}();
|
|
||||||
e += ex;
|
|
||||||
f = f.shiftExponent(-e); // f /= 10^e;
|
|
||||||
|
|
||||||
XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root(Number, unsigned)", "f is normalized");
|
// Scale f into the range (0, 1) such that the scale change (e) is a
|
||||||
|
// multiple of the root (d)
|
||||||
|
auto e = exponent + range.log + 1;
|
||||||
|
auto const di = static_cast<int>(d);
|
||||||
|
auto ex = [e = e, di = di]() // Euclidean remainder of e/d
|
||||||
|
{
|
||||||
|
int k = (e >= 0 ? e : e - (di - 1)) / di;
|
||||||
|
int k2 = e - k * di;
|
||||||
|
if (k2 == 0)
|
||||||
|
return 0;
|
||||||
|
return di - k2;
|
||||||
|
}();
|
||||||
|
e += ex;
|
||||||
|
f = f.shiftExponent(-e); // f /= 10^e;
|
||||||
|
return std::make_tuple(e, di);
|
||||||
|
}();
|
||||||
|
|
||||||
|
XRPL_ASSERT_PARTS(e % di == 0, "xrpl::root(Number, unsigned)", "e is divisible by d");
|
||||||
|
XRPL_ASSERT_PARTS(f.isnormal(range), "xrpl::root(Number, unsigned)", "f is normalized");
|
||||||
bool neg = false;
|
bool neg = false;
|
||||||
if (f < zero)
|
if (f < zero)
|
||||||
{
|
{
|
||||||
@@ -1002,15 +1092,32 @@ root(Number f, unsigned d)
|
|||||||
|
|
||||||
// return r * 10^(e/d) to reverse scaling
|
// return r * 10^(e/d) to reverse scaling
|
||||||
auto const result = r.shiftExponent(e / di);
|
auto const result = r.shiftExponent(e / di);
|
||||||
XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root(Number, unsigned)", "result is normalized");
|
XRPL_ASSERT_PARTS(result.isnormal(range), "xrpl::root(Number, unsigned)", "result is normalized");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns f^(1/d)
|
||||||
|
// Uses Newton–Raphson iterations until the result stops changing
|
||||||
|
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||||
|
|
||||||
|
// This function, and power(Number f, unsigned n, unsigned d)
|
||||||
|
// treat corner cases such as 0 roots as advised by Annex F of
|
||||||
|
// the C standard, which itself is consistent with the IEEE
|
||||||
|
// floating point standards.
|
||||||
|
|
||||||
|
Number
|
||||||
|
root(Number f, unsigned d)
|
||||||
|
{
|
||||||
|
auto const& range = Number::range_.get();
|
||||||
|
return Number::root(range, f, d);
|
||||||
|
}
|
||||||
|
|
||||||
Number
|
Number
|
||||||
root2(Number f)
|
root2(Number f)
|
||||||
{
|
{
|
||||||
|
auto const& range = Number::range_.get();
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
auto const one = Number::one();
|
auto const one = Number::one(range);
|
||||||
|
|
||||||
if (f == one)
|
if (f == one)
|
||||||
return f;
|
return f;
|
||||||
@@ -1019,12 +1126,18 @@ root2(Number f)
|
|||||||
if (f == zero)
|
if (f == zero)
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
auto const e = [&]() {
|
||||||
auto e = f.exponent_ + Number::mantissaLog() + 1;
|
auto const [negative, mantissa, exponent] = f.toInternal(range);
|
||||||
if (e % 2 != 0)
|
|
||||||
++e;
|
// Scale f into the range (0, 1) such that f's exponent is a
|
||||||
f = f.shiftExponent(-e); // f /= 10^e;
|
// multiple of d
|
||||||
XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root2(Number)", "f is normalized");
|
auto e = exponent + range.log + 1;
|
||||||
|
if (e % 2 != 0)
|
||||||
|
++e;
|
||||||
|
f = f.shiftExponent(-e); // f /= 10^e;
|
||||||
|
return e;
|
||||||
|
}();
|
||||||
|
XRPL_ASSERT_PARTS(f.isnormal(range), "xrpl::root2(Number)", "f is normalized");
|
||||||
|
|
||||||
// Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
|
// Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
|
||||||
auto const D = 105;
|
auto const D = 105;
|
||||||
@@ -1046,7 +1159,7 @@ root2(Number f)
|
|||||||
|
|
||||||
// return r * 10^(e/2) to reverse scaling
|
// return r * 10^(e/2) to reverse scaling
|
||||||
auto const result = r.shiftExponent(e / 2);
|
auto const result = r.shiftExponent(e / 2);
|
||||||
XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root2(Number)", "result is normalized");
|
XRPL_ASSERT_PARTS(result.isnormal(range), "xrpl::root2(Number)", "result is normalized");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1056,8 +1169,10 @@ root2(Number f)
|
|||||||
Number
|
Number
|
||||||
power(Number const& f, unsigned n, unsigned d)
|
power(Number const& f, unsigned n, unsigned d)
|
||||||
{
|
{
|
||||||
|
auto const& range = Number::range_.get();
|
||||||
|
|
||||||
constexpr Number zero = Number{};
|
constexpr Number zero = Number{};
|
||||||
auto const one = Number::one();
|
auto const one = Number::one(range);
|
||||||
|
|
||||||
if (f == one)
|
if (f == one)
|
||||||
return f;
|
return f;
|
||||||
@@ -1079,7 +1194,7 @@ power(Number const& f, unsigned n, unsigned d)
|
|||||||
d /= g;
|
d /= g;
|
||||||
if ((n % 2) == 1 && (d % 2) == 0 && f < zero)
|
if ((n % 2) == 1 && (d % 2) == 0 && f < zero)
|
||||||
throw std::overflow_error("Number::power nan");
|
throw std::overflow_error("Number::power nan");
|
||||||
return root(power(f, n), d);
|
return Number::root(range, power(f, n), d);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ public:
|
|||||||
test_limits()
|
test_limits()
|
||||||
{
|
{
|
||||||
auto const scale = Number::getMantissaScale();
|
auto const scale = Number::getMantissaScale();
|
||||||
testcase << "test_limits " << to_string(scale);
|
|
||||||
bool caught = false;
|
|
||||||
auto const minMantissa = Number::minMantissa();
|
auto const minMantissa = Number::minMantissa();
|
||||||
|
|
||||||
|
testcase << "test_limits " << to_string(scale) << ", " << minMantissa;
|
||||||
|
bool caught = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Number x = Number{false, minMantissa * 10, 32768, Number::normalized{}};
|
Number x = Number{false, minMantissa * 10, 32768, Number::normalized{}};
|
||||||
@@ -58,8 +59,9 @@ public:
|
|||||||
__LINE__);
|
__LINE__);
|
||||||
test(Number{false, minMantissa, -32769, Number::normalized{}}, Number{}, __LINE__);
|
test(Number{false, minMantissa, -32769, Number::normalized{}}, Number{}, __LINE__);
|
||||||
test(
|
test(
|
||||||
|
// Use 1501 to force rounding up
|
||||||
Number{false, minMantissa, 32000, Number::normalized{}} * 1'000 +
|
Number{false, minMantissa, 32000, Number::normalized{}} * 1'000 +
|
||||||
Number{false, 1'500, 32000, Number::normalized{}},
|
Number{false, 1'501, 32000, Number::normalized{}},
|
||||||
Number{false, minMantissa + 2, 32003, Number::normalized{}},
|
Number{false, minMantissa + 2, 32003, Number::normalized{}},
|
||||||
__LINE__);
|
__LINE__);
|
||||||
// 9,223,372,036,854,775,808
|
// 9,223,372,036,854,775,808
|
||||||
@@ -159,8 +161,8 @@ public:
|
|||||||
{Number{true, 9'999'999'999'999'999'999ULL, -37, Number::normalized{}},
|
{Number{true, 9'999'999'999'999'999'999ULL, -37, Number::normalized{}},
|
||||||
Number{1'000'000'000'000'000'000, -18},
|
Number{1'000'000'000'000'000'000, -18},
|
||||||
Number{false, 9'999'999'999'999'999'990ULL, -19, Number::normalized{}}},
|
Number{false, 9'999'999'999'999'999'990ULL, -19, Number::normalized{}}},
|
||||||
{Number{Number::maxRep}, Number{6, -1}, Number{Number::maxRep / 10, 1}},
|
{Number{Number::largestMantissa}, Number{6, -1}, Number{Number::largestMantissa / 10, 1}},
|
||||||
{Number{Number::maxRep - 1}, Number{1, 0}, Number{Number::maxRep}},
|
{Number{Number::largestMantissa - 1}, Number{1, 0}, Number{Number::largestMantissa}},
|
||||||
// Test extremes
|
// Test extremes
|
||||||
{
|
{
|
||||||
// Each Number operand rounds up, so the actual mantissa is
|
// Each Number operand rounds up, so the actual mantissa is
|
||||||
@@ -170,11 +172,18 @@ public:
|
|||||||
Number{2, 19},
|
Number{2, 19},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Does not round. Mantissas are going to be > maxRep, so if
|
// Does not round. Mantissas are going to be >
|
||||||
// added together as uint64_t's, the result will overflow.
|
// largestMantissa, so if added together as uint64_t's, the
|
||||||
// With addition using uint128_t, there's no problem. After
|
// result will overflow. With addition using uint128_t,
|
||||||
// normalizing, the resulting mantissa ends up less than
|
// there's no problem. After normalizing, the resulting
|
||||||
// maxRep.
|
// mantissa ends up less than largestMantissa.
|
||||||
|
Number{false, Number::largestMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, Number::largestMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, Number::largestMantissa * 2, 0, Number::normalized{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// These mantissas round down, so adding them together won't
|
||||||
|
// have any consequences.
|
||||||
Number{false, 9'999'999'999'999'999'990ULL, 0, Number::normalized{}},
|
Number{false, 9'999'999'999'999'999'990ULL, 0, Number::normalized{}},
|
||||||
Number{false, 9'999'999'999'999'999'990ULL, 0, Number::normalized{}},
|
Number{false, 9'999'999'999'999'999'990ULL, 0, Number::normalized{}},
|
||||||
Number{false, 1'999'999'999'999'999'998ULL, 1, Number::normalized{}},
|
Number{false, 1'999'999'999'999'999'998ULL, 1, Number::normalized{}},
|
||||||
@@ -261,12 +270,14 @@ public:
|
|||||||
{Number{1'000'000'000'000'000'001, -18},
|
{Number{1'000'000'000'000'000'001, -18},
|
||||||
Number{1'000'000'000'000'000'000, -18},
|
Number{1'000'000'000'000'000'000, -18},
|
||||||
Number{1'000'000'000'000'000'000, -36}},
|
Number{1'000'000'000'000'000'000, -36}},
|
||||||
{Number{Number::maxRep}, Number{6, -1}, Number{Number::maxRep - 1}},
|
{Number{Number::largestMantissa}, Number{6, -1}, Number{Number::largestMantissa - 1}},
|
||||||
{Number{false, Number::maxRep + 1, 0, Number::normalized{}},
|
{Number{false, Number::largestMantissa + 1, 0, Number::normalized{}},
|
||||||
Number{1, 0},
|
Number{1, 0},
|
||||||
Number{Number::maxRep / 10 + 1, 1}},
|
Number{Number::largestMantissa / 10 + 1, 1}},
|
||||||
{Number{false, Number::maxRep + 1, 0, Number::normalized{}}, Number{3, 0}, Number{Number::maxRep}},
|
{Number{false, Number::largestMantissa + 1, 0, Number::normalized{}},
|
||||||
{power(2, 63), Number{3, 0}, Number{Number::maxRep}},
|
Number{3, 0},
|
||||||
|
Number{Number::largestMantissa}},
|
||||||
|
{power(2, 63), Number{3, 0}, Number{Number::largestMantissa}},
|
||||||
});
|
});
|
||||||
auto test = [this](auto const& c) {
|
auto test = [this](auto const& c) {
|
||||||
for (auto const& [x, y, z] : c)
|
for (auto const& [x, y, z] : c)
|
||||||
@@ -289,14 +300,15 @@ public:
|
|||||||
auto const scale = Number::getMantissaScale();
|
auto const scale = Number::getMantissaScale();
|
||||||
testcase << "test_mul " << to_string(scale);
|
testcase << "test_mul " << to_string(scale);
|
||||||
|
|
||||||
using Case = std::tuple<Number, Number, Number>;
|
// Case: Factor 1, Factor 2, Expected product, Line number
|
||||||
|
using Case = std::tuple<Number, Number, Number, int>;
|
||||||
auto test = [this](auto const& c) {
|
auto test = [this](auto const& c) {
|
||||||
for (auto const& [x, y, z] : c)
|
for (auto const& [x, y, z, line] : c)
|
||||||
{
|
{
|
||||||
auto const result = x * y;
|
auto const result = x * y;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << x << " * " << y << " = " << result << ". Expected: " << z;
|
ss << x << " * " << y << " = " << result << ". Expected: " << z;
|
||||||
BEAST_EXPECTS(result == z, ss.str());
|
BEAST_EXPECTS(result == z, ss.str() + " line: " + std::to_string(line));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto tests = [&](auto const& cSmall, auto const& cLarge) {
|
auto tests = [&](auto const& cSmall, auto const& cLarge) {
|
||||||
@@ -306,48 +318,83 @@ public:
|
|||||||
test(cLarge);
|
test(cLarge);
|
||||||
};
|
};
|
||||||
auto const maxMantissa = Number::maxMantissa();
|
auto const maxMantissa = Number::maxMantissa();
|
||||||
|
auto const maxInternalMantissa =
|
||||||
|
static_cast<std::uint64_t>(static_cast<std::int64_t>(power(10, Number::mantissaLog()))) * 10 - 1;
|
||||||
|
|
||||||
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
||||||
{
|
{
|
||||||
auto const cSmall = std::to_array<Case>({
|
auto const cSmall = std::to_array<Case>({
|
||||||
{Number{7}, Number{8}, Number{56}},
|
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{2000000000000000, -15}},
|
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{2000000000000000, -15}, __LINE__},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-2000000000000000, -15}},
|
{Number{-1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{2000000000000000, -15}},
|
Number{1414213562373095, -15},
|
||||||
{Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{1000000000000000, -14}},
|
Number{-2000000000000000, -15},
|
||||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}},
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{2000000000000000, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{1000000000000000, -14}, __LINE__},
|
||||||
|
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}, __LINE__},
|
||||||
// Maximum mantissa range
|
// Maximum mantissa range
|
||||||
{Number{9'999'999'999'999'999, 0}, Number{9'999'999'999'999'999, 0}, Number{9'999'999'999'999'998, 16}},
|
{Number{9'999'999'999'999'999, 0},
|
||||||
|
Number{9'999'999'999'999'999, 0},
|
||||||
|
Number{9'999'999'999'999'998, 16},
|
||||||
|
__LINE__},
|
||||||
});
|
});
|
||||||
auto const cLarge = std::to_array<Case>({
|
auto const cLarge = std::to_array<Case>({
|
||||||
// Note that items with extremely large mantissas need to be
|
// Note that items with extremely large mantissas need to be
|
||||||
// calculated, because otherwise they overflow uint64. Items
|
// calculated, because otherwise they overflow uint64. Items
|
||||||
// from C with larger mantissa
|
// from C with larger mantissa
|
||||||
{Number{7}, Number{8}, Number{56}},
|
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999862, -18}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999862, -18}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999862, -18}},
|
Number{1999999999999999862, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-1999999999999999862, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{1999999999999999862, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{3214285714285706, -15},
|
{Number{3214285714285706, -15},
|
||||||
Number{3111111111111119, -15},
|
Number{3111111111111119, -15},
|
||||||
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}}},
|
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}},
|
||||||
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}},
|
__LINE__},
|
||||||
|
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}, __LINE__},
|
||||||
// Items from cSmall expanded for the larger mantissa,
|
// Items from cSmall expanded for the larger mantissa,
|
||||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||||
// with higher precision
|
// with higher precision
|
||||||
{Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2000000000000000001, -18}},
|
{Number{1414213562373095049, -18},
|
||||||
|
Number{1414213562373095049, -18},
|
||||||
|
Number{2000000000000000001, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{1414213562373095048, -18},
|
Number{1414213562373095048, -18},
|
||||||
Number{-1999999999999999998, -18}},
|
Number{-1999999999999999998, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{-1414213562373095049, -18},
|
Number{-1414213562373095049, -18},
|
||||||
Number{1999999999999999999, -18}},
|
Number{1999999999999999999, -18},
|
||||||
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}},
|
__LINE__},
|
||||||
// Maximum mantissa range - rounds up to 1e19
|
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}, __LINE__},
|
||||||
|
// Maximum internal mantissa range - rounds up to 1e19
|
||||||
|
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{1, 38},
|
||||||
|
__LINE__},
|
||||||
|
// Maximum actual mantissa range - same as int64 range
|
||||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{1, 38}},
|
Number{85'070'591'730'234'615'85, 19},
|
||||||
|
__LINE__},
|
||||||
// Maximum int64 range
|
// Maximum int64 range
|
||||||
{Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'85, 19}},
|
{Number{Number::largestMantissa, 0},
|
||||||
|
Number{Number::largestMantissa, 0},
|
||||||
|
Number{85'070'591'730'234'615'85, 19},
|
||||||
|
__LINE__},
|
||||||
});
|
});
|
||||||
tests(cSmall, cLarge);
|
tests(cSmall, cLarge);
|
||||||
}
|
}
|
||||||
@@ -355,44 +402,78 @@ public:
|
|||||||
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " towards_zero";
|
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " towards_zero";
|
||||||
{
|
{
|
||||||
auto const cSmall = std::to_array<Case>(
|
auto const cSmall = std::to_array<Case>(
|
||||||
{{Number{7}, Number{8}, Number{56}},
|
{{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999, -15}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999, -15}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999, -15}},
|
Number{1999999999999999, -15},
|
||||||
{Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{9999999999999999, -15}},
|
__LINE__},
|
||||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}});
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-1999999999999999, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{1999999999999999, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{3214285714285706, -15},
|
||||||
|
Number{3111111111111119, -15},
|
||||||
|
Number{9999999999999999, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}, __LINE__}});
|
||||||
auto const cLarge = std::to_array<Case>(
|
auto const cLarge = std::to_array<Case>(
|
||||||
// Note that items with extremely large mantissas need to be
|
// Note that items with extremely large mantissas need to be
|
||||||
// calculated, because otherwise they overflow uint64. Items
|
// calculated, because otherwise they overflow uint64. Items
|
||||||
// from C with larger mantissa
|
// from C with larger mantissa
|
||||||
{
|
{
|
||||||
{Number{7}, Number{8}, Number{56}},
|
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999861, -18}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999861, -18}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999861, -18}},
|
Number{1999999999999999861, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-1999999999999999861, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{1999999999999999861, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{3214285714285706, -15},
|
{Number{3214285714285706, -15},
|
||||||
Number{3111111111111119, -15},
|
Number{3111111111111119, -15},
|
||||||
Number{false, 9999999999999999579ULL, -18, Number::normalized{}}},
|
Number{false, 9999999999999999579ULL, -18, Number::normalized{}},
|
||||||
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}},
|
__LINE__},
|
||||||
|
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}, __LINE__},
|
||||||
// Items from cSmall expanded for the larger mantissa,
|
// Items from cSmall expanded for the larger mantissa,
|
||||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||||
// with higher precision
|
// with higher precision
|
||||||
{Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2, 0}},
|
{Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2, 0}, __LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{1414213562373095048, -18},
|
Number{1414213562373095048, -18},
|
||||||
Number{-1999999999999999997, -18}},
|
Number{-1999999999999999997, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{-1414213562373095049, -18},
|
Number{-1414213562373095049, -18},
|
||||||
Number{1999999999999999999, -18}},
|
Number{1999999999999999999, -18},
|
||||||
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}},
|
__LINE__},
|
||||||
// Maximum mantissa range - rounds down to maxMantissa/10e1
|
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}, __LINE__},
|
||||||
|
// Maximum internal mantissa range - rounds down to
|
||||||
|
// maxMantissa/10e1
|
||||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
||||||
|
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, maxInternalMantissa / 10 - 1, 20, Number::normalized{}},
|
||||||
|
__LINE__},
|
||||||
|
// Maximum actual mantissa range - same as int64
|
||||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}},
|
Number{85'070'591'730'234'615'84, 19},
|
||||||
|
__LINE__},
|
||||||
// Maximum int64 range
|
// Maximum int64 range
|
||||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||||
{Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'84, 19}},
|
{Number{Number::largestMantissa, 0},
|
||||||
|
Number{Number::largestMantissa, 0},
|
||||||
|
Number{85'070'591'730'234'615'84, 19},
|
||||||
|
__LINE__},
|
||||||
});
|
});
|
||||||
tests(cSmall, cLarge);
|
tests(cSmall, cLarge);
|
||||||
}
|
}
|
||||||
@@ -400,44 +481,78 @@ public:
|
|||||||
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " downward";
|
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " downward";
|
||||||
{
|
{
|
||||||
auto const cSmall = std::to_array<Case>(
|
auto const cSmall = std::to_array<Case>(
|
||||||
{{Number{7}, Number{8}, Number{56}},
|
{{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999, -15}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-2000000000000000, -15}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999, -15}},
|
Number{1999999999999999, -15},
|
||||||
{Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{9999999999999999, -15}},
|
__LINE__},
|
||||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}});
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-2000000000000000, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{1999999999999999, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{3214285714285706, -15},
|
||||||
|
Number{3111111111111119, -15},
|
||||||
|
Number{9999999999999999, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}, __LINE__}});
|
||||||
auto const cLarge = std::to_array<Case>(
|
auto const cLarge = std::to_array<Case>(
|
||||||
// Note that items with extremely large mantissas need to be
|
// Note that items with extremely large mantissas need to be
|
||||||
// calculated, because otherwise they overflow uint64. Items
|
// calculated, because otherwise they overflow uint64. Items
|
||||||
// from C with larger mantissa
|
// from C with larger mantissa
|
||||||
{
|
{
|
||||||
{Number{7}, Number{8}, Number{56}},
|
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999861, -18}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999862, -18}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999861, -18}},
|
Number{1999999999999999861, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-1999999999999999862, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{1999999999999999861, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{3214285714285706, -15},
|
{Number{3214285714285706, -15},
|
||||||
Number{3111111111111119, -15},
|
Number{3111111111111119, -15},
|
||||||
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}}},
|
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}},
|
||||||
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}},
|
__LINE__},
|
||||||
|
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}, __LINE__},
|
||||||
// Items from cSmall expanded for the larger mantissa,
|
// Items from cSmall expanded for the larger mantissa,
|
||||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||||
// with higher precision
|
// with higher precision
|
||||||
{Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2, 0}},
|
{Number{1414213562373095049, -18}, Number{1414213562373095049, -18}, Number{2, 0}, __LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{1414213562373095048, -18},
|
Number{1414213562373095048, -18},
|
||||||
Number{-1999999999999999998, -18}},
|
Number{-1999999999999999998, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{-1414213562373095049, -18},
|
Number{-1414213562373095049, -18},
|
||||||
Number{1999999999999999999, -18}},
|
Number{1999999999999999999, -18},
|
||||||
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}},
|
__LINE__},
|
||||||
// Maximum mantissa range - rounds down to maxMantissa/10e1
|
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}, __LINE__},
|
||||||
|
// Maximum internal mantissa range - rounds down to
|
||||||
|
// maxMantissa/10-1
|
||||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
||||||
|
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, maxInternalMantissa / 10 - 1, 20, Number::normalized{}},
|
||||||
|
__LINE__},
|
||||||
|
// Maximum mantissa range - same as int64
|
||||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}},
|
Number{85'070'591'730'234'615'84, 19},
|
||||||
|
__LINE__},
|
||||||
// Maximum int64 range
|
// Maximum int64 range
|
||||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||||
{Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'84, 19}},
|
{Number{Number::largestMantissa, 0},
|
||||||
|
Number{Number::largestMantissa, 0},
|
||||||
|
Number{85'070'591'730'234'615'84, 19},
|
||||||
|
__LINE__},
|
||||||
});
|
});
|
||||||
tests(cSmall, cLarge);
|
tests(cSmall, cLarge);
|
||||||
}
|
}
|
||||||
@@ -445,44 +560,80 @@ public:
|
|||||||
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " upward";
|
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " upward";
|
||||||
{
|
{
|
||||||
auto const cSmall = std::to_array<Case>(
|
auto const cSmall = std::to_array<Case>(
|
||||||
{{Number{7}, Number{8}, Number{56}},
|
{{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{2000000000000000, -15}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999, -15}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{2000000000000000, -15}},
|
Number{2000000000000000, -15},
|
||||||
{Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{1000000000000000, -14}},
|
__LINE__},
|
||||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}});
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-1999999999999999, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{2000000000000000, -15},
|
||||||
|
__LINE__},
|
||||||
|
{Number{3214285714285706, -15},
|
||||||
|
Number{3111111111111119, -15},
|
||||||
|
Number{1000000000000000, -14},
|
||||||
|
__LINE__},
|
||||||
|
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}, __LINE__}});
|
||||||
auto const cLarge = std::to_array<Case>(
|
auto const cLarge = std::to_array<Case>(
|
||||||
// Note that items with extremely large mantissas need to be
|
// Note that items with extremely large mantissas need to be
|
||||||
// calculated, because otherwise they overflow uint64. Items
|
// calculated, because otherwise they overflow uint64. Items
|
||||||
// from C with larger mantissa
|
// from C with larger mantissa
|
||||||
{
|
{
|
||||||
{Number{7}, Number{8}, Number{56}},
|
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||||
{Number{1414213562373095, -15}, Number{1414213562373095, -15}, Number{1999999999999999862, -18}},
|
{Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{1414213562373095, -15}, Number{-1999999999999999861, -18}},
|
Number{1414213562373095, -15},
|
||||||
{Number{-1414213562373095, -15}, Number{-1414213562373095, -15}, Number{1999999999999999862, -18}},
|
Number{1999999999999999862, -18},
|
||||||
{Number{3214285714285706, -15}, Number{3111111111111119, -15}, Number{999999999999999958, -17}},
|
__LINE__},
|
||||||
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}},
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-1999999999999999861, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{1999999999999999862, -18},
|
||||||
|
__LINE__},
|
||||||
|
{Number{3214285714285706, -15},
|
||||||
|
Number{3111111111111119, -15},
|
||||||
|
Number{999999999999999958, -17},
|
||||||
|
__LINE__},
|
||||||
|
{Number{1000000000000000000, -32768}, Number{1000000000000000000, -32768}, Number{0}, __LINE__},
|
||||||
// Items from cSmall expanded for the larger mantissa,
|
// Items from cSmall expanded for the larger mantissa,
|
||||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||||
// with higher precision
|
// with higher precision
|
||||||
{Number{1414213562373095049, -18},
|
{Number{1414213562373095049, -18},
|
||||||
Number{1414213562373095049, -18},
|
Number{1414213562373095049, -18},
|
||||||
Number{2000000000000000001, -18}},
|
Number{2000000000000000001, -18},
|
||||||
|
__LINE__},
|
||||||
{Number{-1414213562373095048, -18},
|
{Number{-1414213562373095048, -18},
|
||||||
Number{1414213562373095048, -18},
|
Number{1414213562373095048, -18},
|
||||||
Number{-1999999999999999997, -18}},
|
Number{-1999999999999999997, -18},
|
||||||
{Number{-1414213562373095048, -18}, Number{-1414213562373095049, -18}, Number{2, 0}},
|
__LINE__},
|
||||||
|
{Number{-1414213562373095048, -18}, Number{-1414213562373095049, -18}, Number{2, 0}, __LINE__},
|
||||||
{Number{3214285714285714278, -18},
|
{Number{3214285714285714278, -18},
|
||||||
Number{3111111111111111119, -18},
|
Number{3111111111111111119, -18},
|
||||||
Number{1000000000000000001, -17}},
|
Number{1000000000000000001, -17},
|
||||||
// Maximum mantissa range - rounds up to minMantissa*10
|
__LINE__},
|
||||||
// 1e19*1e19=1e38
|
// Maximum internal mantissa range - rounds up to
|
||||||
|
// minMantissa*10 1e19*1e19=1e38
|
||||||
|
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||||
|
Number{1, 38},
|
||||||
|
__LINE__},
|
||||||
|
// Maximum mantissa range - same as int64
|
||||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||||
Number{1, 38}},
|
Number{85'070'591'730'234'615'85, 19},
|
||||||
|
__LINE__},
|
||||||
// Maximum int64 range
|
// Maximum int64 range
|
||||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||||
{Number{Number::maxRep, 0}, Number{Number::maxRep, 0}, Number{85'070'591'730'234'615'85, 19}},
|
{Number{Number::largestMantissa, 0},
|
||||||
|
Number{Number::largestMantissa, 0},
|
||||||
|
Number{85'070'591'730'234'615'85, 19},
|
||||||
|
__LINE__},
|
||||||
});
|
});
|
||||||
tests(cSmall, cLarge);
|
tests(cSmall, cLarge);
|
||||||
}
|
}
|
||||||
@@ -697,6 +848,9 @@ public:
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
auto const maxInternalMantissa =
|
||||||
|
static_cast<std::uint64_t>(static_cast<std::int64_t>(power(10, Number::mantissaLog()))) * 10 - 1;
|
||||||
|
|
||||||
auto const cSmall = std::to_array<Case>(
|
auto const cSmall = std::to_array<Case>(
|
||||||
{{Number{2}, 2, Number{1414213562373095049, -18}},
|
{{Number{2}, 2, Number{1414213562373095049, -18}},
|
||||||
{Number{2'000'000}, 2, Number{1414213562373095049, -15}},
|
{Number{2'000'000}, 2, Number{1414213562373095049, -15}},
|
||||||
@@ -708,14 +862,14 @@ public:
|
|||||||
{Number{0}, 5, Number{0}},
|
{Number{0}, 5, Number{0}},
|
||||||
{Number{5625, -4}, 2, Number{75, -2}}});
|
{Number{5625, -4}, 2, Number{75, -2}}});
|
||||||
auto const cLarge = std::to_array<Case>({
|
auto const cLarge = std::to_array<Case>({
|
||||||
{Number{false, Number::maxMantissa() - 9, -1, Number::normalized{}},
|
{Number{false, maxInternalMantissa - 9, -1, Number::normalized{}},
|
||||||
2,
|
2,
|
||||||
Number{false, 999'999'999'999'999'999, -9, Number::normalized{}}},
|
Number{false, 999'999'999'999'999'999, -9, Number::normalized{}}},
|
||||||
{Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}},
|
{Number{false, maxInternalMantissa - 9, 0, Number::normalized{}},
|
||||||
2,
|
2,
|
||||||
Number{false, 3'162'277'660'168'379'330, -9, Number::normalized{}}},
|
Number{false, 3'162'277'660'168'379'330, -9, Number::normalized{}}},
|
||||||
{Number{Number::maxRep}, 2, Number{false, 3'037'000'499'976049692, -9, Number::normalized{}}},
|
{Number{Number::largestMantissa}, 2, Number{false, 3'037'000'499'976049692, -9, Number::normalized{}}},
|
||||||
{Number{Number::maxRep}, 4, Number{false, 55'108'98747006743627, -14, Number::normalized{}}},
|
{Number{Number::largestMantissa}, 4, Number{false, 55'108'98747006743627, -14, Number::normalized{}}},
|
||||||
});
|
});
|
||||||
test(cSmall);
|
test(cSmall);
|
||||||
if (Number::getMantissaScale() != MantissaRange::small)
|
if (Number::getMantissaScale() != MantissaRange::small)
|
||||||
@@ -762,6 +916,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto const maxInternalMantissa = power(10, Number::mantissaLog()) * 10 - 1;
|
||||||
|
|
||||||
auto const cSmall = std::to_array<Number>({
|
auto const cSmall = std::to_array<Number>({
|
||||||
Number{2},
|
Number{2},
|
||||||
Number{2'000'000},
|
Number{2'000'000},
|
||||||
@@ -771,7 +927,10 @@ public:
|
|||||||
Number{5, -1},
|
Number{5, -1},
|
||||||
Number{0},
|
Number{0},
|
||||||
Number{5625, -4},
|
Number{5625, -4},
|
||||||
Number{Number::maxRep},
|
Number{Number::largestMantissa},
|
||||||
|
maxInternalMantissa,
|
||||||
|
Number{Number::minMantissa(), 0, Number::unchecked{}},
|
||||||
|
Number{Number::maxMantissa(), 0, Number::unchecked{}},
|
||||||
});
|
});
|
||||||
test(cSmall);
|
test(cSmall);
|
||||||
bool caught = false;
|
bool caught = false;
|
||||||
@@ -1113,16 +1272,16 @@ public:
|
|||||||
case MantissaRange::large:
|
case MantissaRange::large:
|
||||||
// Test the edges
|
// Test the edges
|
||||||
// ((exponent < -(28)) || (exponent > -(8)))))
|
// ((exponent < -(28)) || (exponent > -(8)))))
|
||||||
test(Number::min(), "1e-32750");
|
test(Number::min(), "922337203685477581e-32768");
|
||||||
test(Number::max(), "9223372036854775807e32768");
|
test(Number::max(), "9223372036854775807e32768");
|
||||||
test(Number::lowest(), "-9223372036854775807e32768");
|
test(Number::lowest(), "-9223372036854775807e32768");
|
||||||
{
|
{
|
||||||
NumberRoundModeGuard mg(Number::towards_zero);
|
NumberRoundModeGuard mg(Number::towards_zero);
|
||||||
|
|
||||||
auto const maxMantissa = Number::maxMantissa();
|
auto const maxMantissa = Number::maxMantissa();
|
||||||
BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999'999ULL);
|
BEAST_EXPECT(maxMantissa == 9'223'372'036'854'775'807ULL);
|
||||||
test(Number{false, maxMantissa, 0, Number::normalized{}}, "9999999999999999990");
|
test(Number{false, maxMantissa, 0, Number::normalized{}}, "9223372036854775807");
|
||||||
test(Number{true, maxMantissa, 0, Number::normalized{}}, "-9999999999999999990");
|
test(Number{true, maxMantissa, 0, Number::normalized{}}, "-9223372036854775807");
|
||||||
|
|
||||||
test(Number{std::numeric_limits<std::int64_t>::max(), 0}, "9223372036854775807");
|
test(Number{std::numeric_limits<std::int64_t>::max(), 0}, "9223372036854775807");
|
||||||
test(-(Number{std::numeric_limits<std::int64_t>::max(), 0}), "-9223372036854775807");
|
test(-(Number{std::numeric_limits<std::int64_t>::max(), 0}), "-9223372036854775807");
|
||||||
@@ -1300,7 +1459,7 @@ public:
|
|||||||
Number const initalXrp{INITIAL_XRP};
|
Number const initalXrp{INITIAL_XRP};
|
||||||
BEAST_EXPECT(initalXrp.exponent() > 0);
|
BEAST_EXPECT(initalXrp.exponent() > 0);
|
||||||
|
|
||||||
Number const maxInt64{Number::maxRep};
|
Number const maxInt64{Number::largestMantissa};
|
||||||
BEAST_EXPECT(maxInt64.exponent() > 0);
|
BEAST_EXPECT(maxInt64.exponent() > 0);
|
||||||
// 85'070'591'730'234'615'865'843'651'857'942'052'864 - 38 digits
|
// 85'070'591'730'234'615'865'843'651'857'942'052'864 - 38 digits
|
||||||
BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'62, 22}));
|
BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'62, 22}));
|
||||||
@@ -1317,20 +1476,198 @@ public:
|
|||||||
Number const initalXrp{INITIAL_XRP};
|
Number const initalXrp{INITIAL_XRP};
|
||||||
BEAST_EXPECT(initalXrp.exponent() <= 0);
|
BEAST_EXPECT(initalXrp.exponent() <= 0);
|
||||||
|
|
||||||
Number const maxInt64{Number::maxRep};
|
Number const maxInt64{Number::largestMantissa};
|
||||||
BEAST_EXPECT(maxInt64.exponent() <= 0);
|
BEAST_EXPECT(maxInt64.exponent() <= 0);
|
||||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249 - 38 digits
|
// 85'070'591'730'234'615'847'396'907'784'232'501'249 - 38 digits
|
||||||
BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'615'85, 19}));
|
BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'615'85, 19}));
|
||||||
|
|
||||||
NumberRoundModeGuard mg(Number::towards_zero);
|
NumberRoundModeGuard mg(Number::towards_zero);
|
||||||
|
|
||||||
auto const maxMantissa = Number::maxMantissa();
|
{
|
||||||
Number const max = Number{false, maxMantissa, 0, Number::normalized{}};
|
auto const maxInternalMantissa =
|
||||||
BEAST_EXPECT(max.mantissa() == maxMantissa / 10);
|
static_cast<std::uint64_t>(static_cast<std::int64_t>(power(10, Number::mantissaLog()))) * 10 - 1;
|
||||||
BEAST_EXPECT(max.exponent() == 1);
|
|
||||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38
|
// Rounds down to fit under 2^63
|
||||||
// digits
|
Number const max = Number{false, maxInternalMantissa, 0, Number::normalized{}};
|
||||||
BEAST_EXPECT((power(max, 2) == Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}));
|
// No alterations by the accessors
|
||||||
|
BEAST_EXPECT(max.mantissa() == maxInternalMantissa / 10);
|
||||||
|
BEAST_EXPECT(max.exponent() == 1);
|
||||||
|
// 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38
|
||||||
|
// digits
|
||||||
|
BEAST_EXPECT((power(max, 2) == Number{false, maxInternalMantissa / 10 - 1, 20, Number::normalized{}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto const maxMantissa = Number::maxMantissa();
|
||||||
|
Number const max = Number{false, maxMantissa, 0, Number::normalized{}};
|
||||||
|
// No alterations by the accessors
|
||||||
|
BEAST_EXPECT(max.mantissa() == maxMantissa);
|
||||||
|
BEAST_EXPECT(max.exponent() == 0);
|
||||||
|
// 85'070'591'730'234'615'847'396'907'784'232'501'249 - also 38
|
||||||
|
// digits
|
||||||
|
BEAST_EXPECT((power(max, 2) == Number{false, 85'070'591'730'234'615'84, 19, Number::normalized{}}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testNormalizeToRange()
|
||||||
|
{
|
||||||
|
// Test edge-cases of normalizeToRange
|
||||||
|
auto const scale = Number::getMantissaScale();
|
||||||
|
testcase << "normalizeToRange " << to_string(scale);
|
||||||
|
|
||||||
|
auto test = [this](
|
||||||
|
Number const& n,
|
||||||
|
auto const rangeMin,
|
||||||
|
auto const rangeMax,
|
||||||
|
auto const expectedMantissa,
|
||||||
|
auto const expectedExponent,
|
||||||
|
auto const line) {
|
||||||
|
auto const normalized = n.normalizeToRange(rangeMin, rangeMax);
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
normalized.first == expectedMantissa,
|
||||||
|
"Number " + to_string(n) + " scaled to " + std::to_string(rangeMax) +
|
||||||
|
". Expected mantissa:" + std::to_string(expectedMantissa) +
|
||||||
|
", got: " + std::to_string(normalized.first) + " @ " + std::to_string(line));
|
||||||
|
BEAST_EXPECTS(
|
||||||
|
normalized.second == expectedExponent,
|
||||||
|
"Number " + to_string(n) + " scaled to " + std::to_string(rangeMax) +
|
||||||
|
". Expected exponent:" + std::to_string(expectedExponent) +
|
||||||
|
", got: " + std::to_string(normalized.second) + " @ " + std::to_string(line));
|
||||||
|
};
|
||||||
|
|
||||||
|
std::int64_t constexpr iRangeMin = 100;
|
||||||
|
std::int64_t constexpr iRangeMax = 999;
|
||||||
|
|
||||||
|
std::uint64_t constexpr uRangeMin = 100;
|
||||||
|
std::uint64_t constexpr uRangeMax = 999;
|
||||||
|
|
||||||
|
constexpr static MantissaRange largeRange{MantissaRange::large};
|
||||||
|
|
||||||
|
std::int64_t constexpr iBigMin = largeRange.min;
|
||||||
|
std::int64_t constexpr iBigMax = largeRange.max;
|
||||||
|
|
||||||
|
auto const testSuite = [&](Number const& n,
|
||||||
|
auto const expectedSmallMantissa,
|
||||||
|
auto const expectedSmallExponent,
|
||||||
|
auto const expectedLargeMantissa,
|
||||||
|
auto const expectedLargeExponent,
|
||||||
|
auto const line) {
|
||||||
|
test(n, iRangeMin, iRangeMax, expectedSmallMantissa, expectedSmallExponent, line);
|
||||||
|
test(n, iBigMin, iBigMax, expectedLargeMantissa, expectedLargeExponent, line);
|
||||||
|
|
||||||
|
// Only test non-negative. testing a negative number with an
|
||||||
|
// unsigned range will assert, and asserts can't be tested.
|
||||||
|
if (n.signum() >= 0)
|
||||||
|
{
|
||||||
|
test(n, uRangeMin, uRangeMax, expectedSmallMantissa, expectedSmallExponent, line);
|
||||||
|
test(n, largeRange.min, largeRange.max, expectedLargeMantissa, expectedLargeExponent, line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
// zero
|
||||||
|
Number const n{0};
|
||||||
|
|
||||||
|
testSuite(n, 0, std::numeric_limits<int>::lowest(), 0, std::numeric_limits<int>::lowest(), __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Small positive number
|
||||||
|
Number const n{2};
|
||||||
|
|
||||||
|
testSuite(n, 200, -2, 2'000'000'000'000'000'000, -18, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Negative number
|
||||||
|
Number const n{-2};
|
||||||
|
|
||||||
|
testSuite(n, -200, -2, -2'000'000'000'000'000'000, -18, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Biggest valid mantissa
|
||||||
|
Number const n{Number::largestMantissa, 0, Number::normalized{}};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
// With the small mantissa range, the value rounds up. Because
|
||||||
|
// it rounds up, when scaling up to the full int64 range, it
|
||||||
|
// can't go over the max, so it is one digit smaller than the
|
||||||
|
// full value.
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, 922, 16, Number::largestMantissa, 0, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Biggest valid mantissa + 1
|
||||||
|
Number const n{Number::largestMantissa + 1, 0, Number::normalized{}};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
// With the small mantissa range, the value rounds up. Because
|
||||||
|
// it rounds up, when scaling up to the full int64 range, it
|
||||||
|
// can't go over the max, so it is one digit smaller than the
|
||||||
|
// full value.
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'581, 1, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Biggest valid mantissa + 2
|
||||||
|
Number const n{Number::largestMantissa + 2, 0, Number::normalized{}};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
// With the small mantissa range, the value rounds up. Because
|
||||||
|
// it rounds up, when scaling up to the full int64 range, it
|
||||||
|
// can't go over the max, so it is one digit smaller than the
|
||||||
|
// full value.
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'581, 1, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Biggest valid mantissa + 3
|
||||||
|
Number const n{Number::largestMantissa + 3, 0, Number::normalized{}};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
// With the small mantissa range, the value rounds up. Because
|
||||||
|
// it rounds up, when scaling up to the full int64 range, it
|
||||||
|
// can't go over the max, so it is one digit smaller than the
|
||||||
|
// full value.
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, 922, 16, 922'337'203'685'477'581, 1, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// int64 min
|
||||||
|
Number const n{std::numeric_limits<std::int64_t>::min(), 0};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
testSuite(n, -922, 16, -922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, -922, 16, -922'337'203'685'477'581, 1, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// int64 min + 1
|
||||||
|
Number const n{std::numeric_limits<std::int64_t>::min() + 1, 0};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
testSuite(n, -922, 16, -922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, -922, 16, -9'223'372'036'854'775'807, 0, __LINE__);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// int64 min - 1
|
||||||
|
// Need to cast to uint, even though we're dealing with a negative
|
||||||
|
// number to avoid overflow and UB
|
||||||
|
Number const n{
|
||||||
|
true,
|
||||||
|
-static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::min()) + 1,
|
||||||
|
0,
|
||||||
|
Number::normalized{}};
|
||||||
|
|
||||||
|
if (scale == MantissaRange::small)
|
||||||
|
testSuite(n, -922, 16, -922'337'203'685'477'600, 1, __LINE__);
|
||||||
|
else
|
||||||
|
testSuite(n, -922, 16, -922'337'203'685'477'581, 1, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1361,6 +1698,7 @@ public:
|
|||||||
test_truncate();
|
test_truncate();
|
||||||
testRounding();
|
testRounding();
|
||||||
testInt64();
|
testInt64();
|
||||||
|
testNormalizeToRange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user