mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Continue with Step 1
- Track down and fix edge cases. - Some refactoring and renaming for clarity and simplicity
This commit is contained in:
@@ -22,14 +22,14 @@ template <typename T>
|
||||
constexpr std::optional<int>
|
||||
logTen(T value)
|
||||
{
|
||||
int power = 0;
|
||||
int log = 0;
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
{
|
||||
value /= 10;
|
||||
++power;
|
||||
++log;
|
||||
}
|
||||
if (value == 1)
|
||||
return power;
|
||||
return log;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -50,16 +50,16 @@ using numberint128 = __int128_t;
|
||||
|
||||
struct MantissaRange
|
||||
{
|
||||
using rep = numberint128;
|
||||
using internalrep = numberint128;
|
||||
|
||||
explicit constexpr MantissaRange(rep min_)
|
||||
: min(min_), max(min_ * 10 - 1), power(logTen(min).value_or(-1))
|
||||
explicit constexpr MantissaRange(internalrep min_)
|
||||
: min(min_), max(min_ * 10 - 1), log(logTen(min).value_or(-1))
|
||||
{
|
||||
}
|
||||
|
||||
rep min;
|
||||
rep max;
|
||||
int power;
|
||||
internalrep min;
|
||||
internalrep max;
|
||||
int log;
|
||||
};
|
||||
|
||||
class Number
|
||||
@@ -68,7 +68,7 @@ class Number
|
||||
using int128_t = numberint128;
|
||||
|
||||
using rep = std::int64_t;
|
||||
using internalrep = MantissaRange::rep;
|
||||
using internalrep = MantissaRange::internalrep;
|
||||
internalrep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
@@ -121,11 +121,11 @@ public:
|
||||
operator/=(Number const& x);
|
||||
|
||||
static constexpr Number
|
||||
min(MantissaRange const& range) noexcept;
|
||||
min() noexcept;
|
||||
static constexpr Number
|
||||
max(MantissaRange const& range) noexcept;
|
||||
max() noexcept;
|
||||
static constexpr Number
|
||||
lowest(MantissaRange const& range) noexcept;
|
||||
lowest() noexcept;
|
||||
|
||||
/** Conversions to Number are implicit and conversions away from Number
|
||||
* are explicit. This design encourages and facilitates the use of Number
|
||||
@@ -194,7 +194,7 @@ public:
|
||||
while (ret.exponent_ < 0 && ret.mantissa_ != 0)
|
||||
{
|
||||
ret.exponent_ += 1;
|
||||
ret.mantissa_ /= rep(10);
|
||||
ret.mantissa_ /= internalrep(10);
|
||||
}
|
||||
// We are guaranteed that normalize() will never throw an exception
|
||||
// because exponent is either negative or zero at this point.
|
||||
@@ -249,17 +249,21 @@ public:
|
||||
return range_.get().max;
|
||||
}
|
||||
|
||||
inline static internalrep
|
||||
mantissaPower()
|
||||
inline static int
|
||||
mantissaLog()
|
||||
{
|
||||
return range_.get().power;
|
||||
return range_.get().log;
|
||||
}
|
||||
|
||||
/// oneSmall is needed because the ranges are private
|
||||
constexpr static Number
|
||||
oneSmall();
|
||||
/// oneLarge is needed because the ranges are private
|
||||
constexpr static Number
|
||||
oneLarge();
|
||||
|
||||
// And one is needed because it needs to choose between oneSmall and
|
||||
// oneLarge based on the current range
|
||||
static Number
|
||||
one();
|
||||
|
||||
@@ -275,10 +279,12 @@ private:
|
||||
constexpr static MantissaRange smallRange{1'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(smallRange.min));
|
||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||
static_assert(smallRange.log == 15);
|
||||
// maxint64 9,223,372,036,854,775,808
|
||||
constexpr static MantissaRange largeRange{1'000'000'000'000'000'000LL};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(largeRange.log == 18);
|
||||
static_assert(largeRange.min < std::numeric_limits<std::int64_t>::max());
|
||||
static_assert(largeRange.max > std::numeric_limits<std::int64_t>::max());
|
||||
|
||||
@@ -298,12 +304,7 @@ private:
|
||||
internalrep const& maxMantissa);
|
||||
|
||||
constexpr bool
|
||||
isnormal(MantissaRange const& range) const noexcept;
|
||||
|
||||
friend Number
|
||||
root(Number f, unsigned d);
|
||||
friend Number
|
||||
root2(Number f);
|
||||
isnormal() const noexcept;
|
||||
|
||||
class Guard;
|
||||
};
|
||||
@@ -421,26 +422,27 @@ operator/(Number const& x, Number const& y)
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::min(MantissaRange const& range) noexcept
|
||||
Number::min() noexcept
|
||||
{
|
||||
return Number{range.min, minExponent, unchecked{}};
|
||||
return Number{range_.get().min, minExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::max(MantissaRange const& range) noexcept
|
||||
Number::max() noexcept
|
||||
{
|
||||
return Number{range.max, maxExponent, unchecked{}};
|
||||
return Number{range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::lowest(MantissaRange const& range) noexcept
|
||||
Number::lowest() noexcept
|
||||
{
|
||||
return -Number{range.max, maxExponent, unchecked{}};
|
||||
return -Number{range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline constexpr bool
|
||||
Number::isnormal(MantissaRange const& range) const noexcept
|
||||
Number::isnormal() const noexcept
|
||||
{
|
||||
MantissaRange const& range = range_;
|
||||
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
|
||||
return range.min <= abs_m && abs_m <= range.max &&
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent;
|
||||
|
||||
@@ -14,7 +14,22 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("Using boost::multiprecision::uint128_t")
|
||||
#endif
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
using uint128_t = boost::multiprecision::uint128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using uint128_t = __uint128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
static_assert(std::is_same_v<uint128_t, ripple::numberuint128>);
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct make_unsigned<ripple::numberint128>
|
||||
{
|
||||
using type = typename uint128_t;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -61,14 +76,9 @@ public:
|
||||
is_negative() const noexcept;
|
||||
|
||||
// add a digit
|
||||
template <class T>
|
||||
void
|
||||
push(numberuint128 d) noexcept;
|
||||
#ifdef _MSC_VER
|
||||
void
|
||||
push(numberint128 d) noexcept;
|
||||
void
|
||||
push(std::int64_t d) noexcept;
|
||||
#endif
|
||||
push(T d) noexcept;
|
||||
|
||||
// recover a digit
|
||||
unsigned
|
||||
@@ -79,6 +89,10 @@ public:
|
||||
// tie, round towards even.
|
||||
int
|
||||
round() noexcept;
|
||||
|
||||
private:
|
||||
void
|
||||
doPush(unsigned d) noexcept;
|
||||
};
|
||||
|
||||
inline void
|
||||
@@ -100,29 +114,20 @@ Number::Guard::is_negative() const noexcept
|
||||
}
|
||||
|
||||
inline void
|
||||
Number::Guard::push(numberuint128 d128) noexcept
|
||||
Number::Guard::doPush(unsigned d) noexcept
|
||||
{
|
||||
unsigned d = static_cast<unsigned>(d128);
|
||||
|
||||
xbit_ = xbit_ || (digits_ & 0x0000'0000'0000'000F) != 0;
|
||||
digits_ >>= 4;
|
||||
digits_ |= (d & 0x0000'0000'0000'000FULL) << 60;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
void
|
||||
Number::Guard::push(numberint128 d) noexcept
|
||||
template <class T>
|
||||
inline void
|
||||
Number::Guard::push(T d) noexcept
|
||||
{
|
||||
push(static_cast<numberuint128>(d));
|
||||
doPush(static_cast<unsigned>(d));
|
||||
}
|
||||
|
||||
void
|
||||
Number::Guard::push(std::int64_t d) noexcept
|
||||
{
|
||||
push(static_cast<numberuint128>(d));
|
||||
}
|
||||
#endif
|
||||
|
||||
inline unsigned
|
||||
Number::Guard::pop() noexcept
|
||||
{
|
||||
@@ -178,23 +183,27 @@ constexpr Number
|
||||
Number::oneSmall()
|
||||
{
|
||||
return Number{
|
||||
Number::smallRange.min, -Number::smallRange.power, Number::unchecked{}};
|
||||
Number::smallRange.min, -Number::smallRange.log, Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number oneSml = Number::oneSmall();
|
||||
|
||||
constexpr Number
|
||||
Number::oneLarge()
|
||||
{
|
||||
return Number{
|
||||
Number::largeRange.min, -Number::largeRange.power, Number::unchecked{}};
|
||||
Number::largeRange.min, -Number::largeRange.log, Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number oneLrg = Number::oneLarge();
|
||||
|
||||
Number
|
||||
Number::one()
|
||||
{
|
||||
if (&range_.get() == &smallRange)
|
||||
return oneSmall();
|
||||
return oneSml;
|
||||
XRPL_ASSERT(&range_.get() == &largeRange, "Number::one() : valid range_");
|
||||
return oneLarge();
|
||||
return oneLrg;
|
||||
}
|
||||
|
||||
// Use the member names in this static function for now so the diff is cleaner
|
||||
@@ -205,17 +214,16 @@ Number::normalize(
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa)
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
if (mantissa_ == 0)
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
return;
|
||||
}
|
||||
bool const negative = (mantissa_ < 0);
|
||||
auto m = static_cast<std::make_unsigned_t<rep>>(mantissa_);
|
||||
if (negative)
|
||||
m = -m;
|
||||
auto m = static_cast<std::make_unsigned_t<internalrep>>(
|
||||
negative ? -mantissa_ : mantissa_);
|
||||
while ((m < minMantissa) && (exponent_ > minExponent))
|
||||
{
|
||||
m *= 10;
|
||||
@@ -235,7 +243,6 @@ Number::normalize(
|
||||
mantissa_ = m;
|
||||
if ((exponent_ < minExponent) || (mantissa_ < minMantissa))
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
return;
|
||||
@@ -281,7 +288,7 @@ Number::operator+=(Number const& y)
|
||||
return *this;
|
||||
}
|
||||
XRPL_ASSERT(
|
||||
isnormal(Number::range_) && y.isnormal(Number::range_),
|
||||
isnormal() && y.isnormal(),
|
||||
"ripple::Number::operator+=(Number) : is normal");
|
||||
auto xm = mantissa();
|
||||
auto xe = exponent();
|
||||
@@ -394,7 +401,7 @@ Number::operator+=(Number const& y)
|
||||
// Derived from Hacker's Delight Second Edition Chapter 10
|
||||
// by Henry S. Warren, Jr.
|
||||
static inline unsigned
|
||||
divu10(numberuint128& u)
|
||||
divu10(uint128_t& u)
|
||||
{
|
||||
// q = u * 0.75
|
||||
auto q = (u >> 1) + (u >> 2);
|
||||
@@ -426,7 +433,7 @@ Number::operator*=(Number const& y)
|
||||
return *this;
|
||||
}
|
||||
XRPL_ASSERT(
|
||||
isnormal(Number::range_) && y.isnormal(Number::range_),
|
||||
isnormal() && y.isnormal(),
|
||||
"ripple::Number::operator*=(Number) : is normal");
|
||||
auto xm = mantissa();
|
||||
auto xe = exponent();
|
||||
@@ -444,7 +451,7 @@ Number::operator*=(Number const& y)
|
||||
ym = -ym;
|
||||
yn = -1;
|
||||
}
|
||||
auto zm = numberuint128(xm) * numberuint128(ym);
|
||||
auto zm = uint128_t(xm) * uint128_t(ym);
|
||||
auto ze = xe + ye;
|
||||
auto zn = xn * yn;
|
||||
Guard g;
|
||||
@@ -461,7 +468,7 @@ Number::operator*=(Number const& y)
|
||||
g.push(divu10(zm));
|
||||
++ze;
|
||||
}
|
||||
xm = static_cast<rep>(zm);
|
||||
xm = static_cast<internalrep>(zm);
|
||||
xe = ze;
|
||||
auto r = g.round();
|
||||
if (r == 1 || (r == 0 && (xm & 1) == 1))
|
||||
@@ -485,7 +492,7 @@ Number::operator*=(Number const& y)
|
||||
mantissa_ = xm * zn;
|
||||
exponent_ = xe;
|
||||
XRPL_ASSERT(
|
||||
isnormal(Number::range_) || *this == Number{},
|
||||
isnormal() || *this == Number{},
|
||||
"ripple::Number::operator*=(Number) : result is normal");
|
||||
return *this;
|
||||
}
|
||||
@@ -514,12 +521,15 @@ Number::operator/=(Number const& y)
|
||||
dp = -1;
|
||||
}
|
||||
// Shift by 10^17 gives greatest precision while not overflowing
|
||||
// numberuint128 or the cast back to int64_t
|
||||
// uint128_t or the cast back to int64_t
|
||||
// TODO: Can/should this be made bigger for largeRange?
|
||||
constexpr numberuint128 f = 100'000'000'000'000'000;
|
||||
// log(2^127,10) ~ 38.2
|
||||
// largeRange.log = 18
|
||||
// f can be up to 10^(37-18) = 10^19 safely
|
||||
constexpr uint128_t f = 100'000'000'000'000'000;
|
||||
static_assert(f == smallRange.min * 100);
|
||||
static_assert(smallRange.power == 15);
|
||||
mantissa_ = numberuint128(nm) * f / numberuint128(dm);
|
||||
static_assert(smallRange.log == 15);
|
||||
mantissa_ = uint128_t(nm) * f / uint128_t(dm);
|
||||
exponent_ = ne - de - 17;
|
||||
mantissa_ *= np * dp;
|
||||
normalize();
|
||||
@@ -543,14 +553,19 @@ Number::operator rep() const
|
||||
g.push(drops % 10);
|
||||
drops /= 10;
|
||||
}
|
||||
if (offset == 0 && drops > std::numeric_limits<rep>::max())
|
||||
{
|
||||
// If offset == 0, then the loop won't run, and the overflow check
|
||||
// won't be made, but a int128 can overflow int64 by itself, so
|
||||
// check here.
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
}
|
||||
for (; offset > 0; --offset)
|
||||
{
|
||||
if (drops > std::numeric_limits<rep>::max() / 10)
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
drops *= 10;
|
||||
}
|
||||
if (drops > std::numeric_limits<rep>::max() / 10)
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
auto r = g.round();
|
||||
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
||||
{
|
||||
@@ -573,9 +588,9 @@ to_string(Number const& amount)
|
||||
auto mantissa = amount.mantissa();
|
||||
|
||||
// Use scientific notation for exponents that are too small or too large
|
||||
auto const rangePower = Number::mantissaPower();
|
||||
auto const rangeLog = Number::mantissaLog();
|
||||
if (((exponent != 0) &&
|
||||
((exponent < -(rangePower + 10)) || (exponent > -(rangePower - 10)))))
|
||||
((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
|
||||
{
|
||||
std::string ret = to_string(mantissa);
|
||||
ret.append(1, 'e');
|
||||
@@ -591,11 +606,12 @@ to_string(Number const& amount)
|
||||
negative = true;
|
||||
}
|
||||
|
||||
// TODO: These numbers are probably wrong for largeRange
|
||||
XRPL_ASSERT(
|
||||
exponent + 43 > 0, "ripple::to_string(Number) : minimum exponent");
|
||||
|
||||
ptrdiff_t const pad_prefix = 27;
|
||||
ptrdiff_t const pad_suffix = 23;
|
||||
ptrdiff_t const pad_prefix = Number::mantissaLog() + 12;
|
||||
ptrdiff_t const pad_suffix = Number::mantissaLog() + 8;
|
||||
|
||||
std::string const raw_value(to_string(mantissa));
|
||||
std::string val;
|
||||
@@ -605,7 +621,7 @@ to_string(Number const& amount)
|
||||
val.append(raw_value);
|
||||
val.append(pad_suffix, '0');
|
||||
|
||||
ptrdiff_t const offset(exponent + 43);
|
||||
ptrdiff_t const offset(exponent + pad_prefix + 16);
|
||||
|
||||
auto pre_from(val.begin());
|
||||
auto const pre_to(val.begin() + offset);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
//
|
||||
#include <xrpl/basics/LocalValue.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
@@ -40,11 +42,13 @@ setSTNumberSwitchover(bool v)
|
||||
}
|
||||
|
||||
/* The range for the mantissa when normalized */
|
||||
static std::int64_t constexpr minMantissa = 1'000'000'000'000'000ull;
|
||||
static std::int64_t constexpr maxMantissa = minMantissa * 10 - 1;
|
||||
// log(2^63,10) ~ 18.96
|
||||
//
|
||||
static std::int64_t constexpr minMantissa = STAmount::cMinValue;
|
||||
static std::int64_t constexpr maxMantissa = STAmount::cMaxValue;
|
||||
/* The range for the exponent when normalized */
|
||||
static int constexpr minExponent = -96;
|
||||
static int constexpr maxExponent = 80;
|
||||
static int constexpr minExponent = STAmount::cMinOffset;
|
||||
static int constexpr maxExponent = STAmount::cMaxOffset;
|
||||
|
||||
std::pair<IOUAmount::mantissa_type, IOUAmount::exponent_type>
|
||||
IOUAmount::scaleNumber(Number const& number)
|
||||
|
||||
@@ -310,8 +310,8 @@ STAmount&
|
||||
STAmount::operator=(IOUAmount const& iou)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
native() == false,
|
||||
"ripple::STAmount::operator=(IOUAmount) : is not XRP");
|
||||
integral() == false,
|
||||
"ripple::STAmount::operator=(IOUAmount) : is not integral");
|
||||
mOffset = iou.exponent();
|
||||
mIsNegative = iou < beast::zero;
|
||||
if (mIsNegative)
|
||||
@@ -851,8 +851,9 @@ STAmount::canonicalize()
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
auto const value = unsafe_cast<std::int64_t>(mValue);
|
||||
Number num(
|
||||
mIsNegative ? -mValue : mValue, mOffset, Number::unchecked{});
|
||||
mIsNegative ? -value : value, mOffset, Number::unchecked{});
|
||||
auto set = [&](auto const& val) {
|
||||
mIsNegative = val.value() < 0;
|
||||
mValue = mIsNegative ? -val.value() : val.value();
|
||||
|
||||
@@ -50,11 +50,25 @@ STNumber::add(Serializer& s) const
|
||||
XRPL_ASSERT(
|
||||
getFName().fieldType == getSType(),
|
||||
"ripple::STNumber::add : field type match");
|
||||
constexpr std::int64_t min = 100'000'000'000'000'000LL;
|
||||
constexpr std::int64_t max = min * 10 - 1;
|
||||
auto const [mantissa, exponent] = value_.normalizeToRange(min, max);
|
||||
s.add64(mantissa);
|
||||
s.add32(exponent);
|
||||
if (value_.mantissa() <= std::numeric_limits<std::int64_t>::max() &&
|
||||
value_.mantissa() >= std::numeric_limits<std::int64_t>::min())
|
||||
{
|
||||
// If the mantissa fits in the range of std::int64_t, write it directly.
|
||||
// This preserves the maximum available precision.
|
||||
// With the small range, all numbers should be written this way. With
|
||||
// the large range, it's likely that most numbers will be written this
|
||||
// way.
|
||||
s.add64(static_cast<std::int64_t>(value_.mantissa()));
|
||||
s.add32(value_.exponent());
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr std::int64_t min = 100'000'000'000'000'000LL;
|
||||
constexpr std::int64_t max = min * 10 - 1;
|
||||
auto const [mantissa, exponent] = value_.normalizeToRange(min, max);
|
||||
s.add64(mantissa);
|
||||
s.add32(exponent);
|
||||
}
|
||||
}
|
||||
|
||||
Number const&
|
||||
|
||||
@@ -17,14 +17,15 @@ public:
|
||||
{
|
||||
testcase("zero");
|
||||
|
||||
Number const z{0, 0};
|
||||
for (Number const& z : {Number{0, 0}, Number{0}})
|
||||
{
|
||||
BEAST_EXPECT(z.mantissa() == 0);
|
||||
BEAST_EXPECT(z.exponent() == Number{}.exponent());
|
||||
|
||||
BEAST_EXPECT(z.mantissa() == 0);
|
||||
BEAST_EXPECT(z.exponent() == Number{}.exponent());
|
||||
|
||||
BEAST_EXPECT((z + z) == z);
|
||||
BEAST_EXPECT((z - z) == z);
|
||||
BEAST_EXPECT(z == -z);
|
||||
BEAST_EXPECT((z + z) == z);
|
||||
BEAST_EXPECT((z - z) == z);
|
||||
BEAST_EXPECT(z == -z);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -738,12 +739,12 @@ public:
|
||||
|
||||
BEAST_EXPECT(
|
||||
std::numeric_limits<std::int64_t>::max() > INITIAL_XRP.drops());
|
||||
BEAST_EXPECT(Number::maxMantissa() > INITIAL_XRP.drops());
|
||||
BEAST_EXPECT(Number::maxMantissa() < INITIAL_XRP.drops());
|
||||
Number initalXrp{INITIAL_XRP};
|
||||
BEAST_EXPECT(initalXrp.exponent() <= 0);
|
||||
BEAST_EXPECT(initalXrp.exponent() > 0);
|
||||
|
||||
Number maxInt64{std::numeric_limits<std::int64_t>::max()};
|
||||
BEAST_EXPECT(maxInt64.exponent() <= 0);
|
||||
BEAST_EXPECT(maxInt64.exponent() > 0);
|
||||
|
||||
// TODO: square maxInt64 and square Number::max()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user