mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-23 12:35:50 +00:00
Add tests
This commit is contained in:
committed by
Elliot Lee
parent
c9c54c9799
commit
48e804c40c
@@ -37,7 +37,7 @@ class Number
|
|||||||
{
|
{
|
||||||
using rep = std::int64_t;
|
using rep = std::int64_t;
|
||||||
rep mantissa_{0};
|
rep mantissa_{0};
|
||||||
int exponent_{-2'147'483'648};
|
int exponent_{std::numeric_limits<int>::lowest()};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct unchecked
|
struct unchecked
|
||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
explicit unchecked() = default;
|
explicit unchecked() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Number() = default;
|
explicit constexpr Number() = default;
|
||||||
|
|
||||||
Number(rep mantissa);
|
Number(rep mantissa);
|
||||||
explicit Number(rep mantissa, int exponent);
|
explicit Number(rep mantissa, int exponent);
|
||||||
@@ -166,7 +166,7 @@ private:
|
|||||||
constexpr static int minExponent = -32768;
|
constexpr static int minExponent = -32768;
|
||||||
constexpr static int maxExponent = 32768;
|
constexpr static int maxExponent = 32768;
|
||||||
|
|
||||||
class guard;
|
class Guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
|
inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
|
||||||
@@ -308,10 +308,10 @@ abs(Number x) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns f^n
|
// Returns f^n
|
||||||
// Uses a log_2(n) number of mulitiplications
|
// Uses a log_2(n) number of multiplications
|
||||||
|
|
||||||
Number
|
Number
|
||||||
power(Number f, unsigned n);
|
power(Number const& f, unsigned n);
|
||||||
|
|
||||||
// Returns f^(1/d)
|
// Returns f^(1/d)
|
||||||
// Uses Newton–Raphson iterations until the result stops changing
|
// Uses Newton–Raphson iterations until the result stops changing
|
||||||
@@ -323,12 +323,12 @@ root(Number f, unsigned d);
|
|||||||
// Returns f^(n/d)
|
// Returns f^(n/d)
|
||||||
|
|
||||||
Number
|
Number
|
||||||
power(Number f, unsigned n, unsigned d);
|
power(Number const& f, unsigned n, unsigned d);
|
||||||
|
|
||||||
// Return 0 if abs(x) < limit, else returns x
|
// Return 0 if abs(x) < limit, else returns x
|
||||||
|
|
||||||
inline constexpr Number
|
inline constexpr Number
|
||||||
clip(Number const& x, Number const& limit) noexcept
|
squelch(Number const& x, Number const& limit) noexcept
|
||||||
{
|
{
|
||||||
if (abs(x) < limit)
|
if (abs(x) < limit)
|
||||||
return Number{};
|
return Number{};
|
||||||
|
|||||||
@@ -33,53 +33,66 @@ using uint128_t = __uint128_t;
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
// guard
|
// Guard
|
||||||
|
|
||||||
class Number::guard
|
// The Guard class is used to tempoarily add extra digits of
|
||||||
|
// preicision to an operation. This enables the final result
|
||||||
|
// to be correctly rounded to the internal precision of Number.
|
||||||
|
|
||||||
|
class Number::Guard
|
||||||
{
|
{
|
||||||
std::uint64_t digits_;
|
std::uint64_t digits_; // 16 decimal guard digits
|
||||||
std::uint8_t xbit_ : 1;
|
std::uint8_t xbit_ : 1; // has a non-zero digit been shifted off the end
|
||||||
std::uint8_t sbit_ : 1; // TODO : get rid of
|
std::uint8_t sbit_ : 1; // the sign of the guard digits
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit guard() : digits_{0}, xbit_{0}, sbit_{0}
|
explicit Guard() : digits_{0}, xbit_{0}, sbit_{0}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set & test the sign bit
|
||||||
void
|
void
|
||||||
set_positive() noexcept;
|
set_positive() noexcept;
|
||||||
void
|
void
|
||||||
set_negative() noexcept;
|
set_negative() noexcept;
|
||||||
bool
|
bool
|
||||||
is_negative() const noexcept;
|
is_negative() const noexcept;
|
||||||
|
|
||||||
|
// add a digit
|
||||||
void
|
void
|
||||||
push(unsigned d) noexcept;
|
push(unsigned d) noexcept;
|
||||||
|
|
||||||
|
// recover a digit
|
||||||
unsigned
|
unsigned
|
||||||
pop() noexcept;
|
pop() noexcept;
|
||||||
|
|
||||||
|
// Indicate round direction: 1 is up, -1 is down, 0 is even
|
||||||
|
// This enables the client to round towards nearest, and on
|
||||||
|
// tie, round towards even.
|
||||||
int
|
int
|
||||||
round() noexcept;
|
round() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
Number::guard::set_positive() noexcept
|
Number::Guard::set_positive() noexcept
|
||||||
{
|
{
|
||||||
sbit_ = 0;
|
sbit_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
Number::guard::set_negative() noexcept
|
Number::Guard::set_negative() noexcept
|
||||||
{
|
{
|
||||||
sbit_ = 1;
|
sbit_ = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
Number::guard::is_negative() const noexcept
|
Number::Guard::is_negative() const noexcept
|
||||||
{
|
{
|
||||||
return sbit_ == 1;
|
return sbit_ == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
Number::guard::push(unsigned d) noexcept
|
Number::Guard::push(unsigned d) noexcept
|
||||||
{
|
{
|
||||||
xbit_ = xbit_ || (digits_ & 0x0000'0000'0000'000F) != 0;
|
xbit_ = xbit_ || (digits_ & 0x0000'0000'0000'000F) != 0;
|
||||||
digits_ >>= 4;
|
digits_ >>= 4;
|
||||||
@@ -87,7 +100,7 @@ Number::guard::push(unsigned d) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned
|
inline unsigned
|
||||||
Number::guard::pop() noexcept
|
Number::Guard::pop() noexcept
|
||||||
{
|
{
|
||||||
unsigned d = (digits_ & 0xF000'0000'0000'0000) >> 60;
|
unsigned d = (digits_ & 0xF000'0000'0000'0000) >> 60;
|
||||||
digits_ <<= 4;
|
digits_ <<= 4;
|
||||||
@@ -95,7 +108,7 @@ Number::guard::pop() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Number::guard::round() noexcept
|
Number::Guard::round() noexcept
|
||||||
{
|
{
|
||||||
if (digits_ > 0x5000'0000'0000'0000)
|
if (digits_ > 0x5000'0000'0000'0000)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -127,10 +140,12 @@ Number::normalize()
|
|||||||
m *= 10;
|
m *= 10;
|
||||||
--exponent_;
|
--exponent_;
|
||||||
}
|
}
|
||||||
|
Guard g;
|
||||||
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);
|
||||||
m /= 10;
|
m /= 10;
|
||||||
++exponent_;
|
++exponent_;
|
||||||
}
|
}
|
||||||
@@ -141,6 +156,16 @@ Number::normalize()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto r = g.round();
|
||||||
|
if (r == 1 || (r == 0 && (mantissa_ & 1) == 1))
|
||||||
|
{
|
||||||
|
++mantissa_;
|
||||||
|
if (mantissa_ > maxMantissa)
|
||||||
|
{
|
||||||
|
mantissa_ /= 10;
|
||||||
|
++exponent_;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (exponent_ > maxExponent)
|
if (exponent_ > maxExponent)
|
||||||
throw std::overflow_error("Number::normalize 2");
|
throw std::overflow_error("Number::normalize 2");
|
||||||
|
|
||||||
@@ -180,7 +205,7 @@ Number::operator+=(Number const& y)
|
|||||||
ym = -ym;
|
ym = -ym;
|
||||||
yn = -1;
|
yn = -1;
|
||||||
}
|
}
|
||||||
guard g;
|
Guard g;
|
||||||
if (xe < ye)
|
if (xe < ye)
|
||||||
{
|
{
|
||||||
if (xn == -1)
|
if (xn == -1)
|
||||||
@@ -261,7 +286,6 @@ Number::operator+=(Number const& y)
|
|||||||
}
|
}
|
||||||
mantissa_ = xm * xn;
|
mantissa_ = xm * xn;
|
||||||
exponent_ = xe;
|
exponent_ = xe;
|
||||||
assert(isnormal());
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +319,7 @@ Number::operator*=(Number const& y)
|
|||||||
auto zm = uint128_t(xm) * uint128_t(ym);
|
auto zm = uint128_t(xm) * uint128_t(ym);
|
||||||
auto ze = xe + ye;
|
auto ze = xe + ye;
|
||||||
auto zn = xn * yn;
|
auto zn = xn * yn;
|
||||||
guard g;
|
Guard g;
|
||||||
while (zm > maxMantissa)
|
while (zm > maxMantissa)
|
||||||
{
|
{
|
||||||
g.push(static_cast<unsigned>(zm % 10));
|
g.push(static_cast<unsigned>(zm % 10));
|
||||||
@@ -379,7 +403,7 @@ Number::operator rep() const
|
|||||||
{
|
{
|
||||||
rep drops = mantissa_;
|
rep drops = mantissa_;
|
||||||
int offset = exponent_;
|
int offset = exponent_;
|
||||||
guard g;
|
Guard g;
|
||||||
if (drops != 0)
|
if (drops != 0)
|
||||||
{
|
{
|
||||||
if (drops < 0)
|
if (drops < 0)
|
||||||
@@ -395,7 +419,7 @@ Number::operator rep() const
|
|||||||
for (; offset > 0; --offset)
|
for (; offset > 0; --offset)
|
||||||
{
|
{
|
||||||
if (drops > std::numeric_limits<decltype(drops)>::max() / 10)
|
if (drops > std::numeric_limits<decltype(drops)>::max() / 10)
|
||||||
throw std::runtime_error("Number::operator rep() overflow");
|
throw std::overflow_error("Number::operator rep() overflow");
|
||||||
drops *= 10;
|
drops *= 10;
|
||||||
}
|
}
|
||||||
auto r = g.round();
|
auto r = g.round();
|
||||||
@@ -505,10 +529,10 @@ to_string(Number const& amount)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns f^n
|
// Returns f^n
|
||||||
// Uses a log_2(n) number of mulitiplications
|
// Uses a log_2(n) number of multiplications
|
||||||
|
|
||||||
Number
|
Number
|
||||||
power(Number f, unsigned n)
|
power(Number const& f, unsigned n)
|
||||||
{
|
{
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return one;
|
return one;
|
||||||
@@ -525,6 +549,11 @@ power(Number f, unsigned n)
|
|||||||
// Uses Newton–Raphson iterations until the result stops changing
|
// Uses Newton–Raphson iterations until the result stops changing
|
||||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
// 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)
|
root(Number f, unsigned d)
|
||||||
{
|
{
|
||||||
@@ -590,10 +619,48 @@ root(Number f, unsigned d)
|
|||||||
return Number{r.mantissa(), r.exponent() + e / di};
|
return Number{r.mantissa(), r.exponent() + e / di};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Number
|
||||||
|
root2(Number f)
|
||||||
|
{
|
||||||
|
if (f == one)
|
||||||
|
return f;
|
||||||
|
if (f < Number{})
|
||||||
|
throw std::overflow_error("Number::root nan");
|
||||||
|
if (f == Number{})
|
||||||
|
return f;
|
||||||
|
|
||||||
|
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
||||||
|
auto e = f.exponent() + 16;
|
||||||
|
if (e % 2 != 0)
|
||||||
|
++e;
|
||||||
|
f = Number{f.mantissa(), f.exponent() - e}; // f /= 10^e;
|
||||||
|
|
||||||
|
// Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
|
||||||
|
auto const D = 105;
|
||||||
|
auto const a0 = 18;
|
||||||
|
auto const a1 = 144;
|
||||||
|
auto const a2 = -60;
|
||||||
|
Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D};
|
||||||
|
|
||||||
|
// Newton–Raphson iteration of f^(1/2) with initial guess r
|
||||||
|
// halt when r stops changing, checking for bouncing on the last iteration
|
||||||
|
Number rm1{};
|
||||||
|
Number rm2{};
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rm2 = rm1;
|
||||||
|
rm1 = r;
|
||||||
|
r = (r + f / r) / Number(2);
|
||||||
|
} while (r != rm1 && r != rm2);
|
||||||
|
|
||||||
|
// return r * 10^(e/2) to reverse scaling
|
||||||
|
return Number{r.mantissa(), r.exponent() + e / 2};
|
||||||
|
}
|
||||||
|
|
||||||
// Returns f^(n/d)
|
// Returns f^(n/d)
|
||||||
|
|
||||||
Number
|
Number
|
||||||
power(Number f, unsigned n, unsigned d)
|
power(Number const& f, unsigned n, unsigned d)
|
||||||
{
|
{
|
||||||
if (f == one)
|
if (f == one)
|
||||||
return f;
|
return f;
|
||||||
@@ -606,9 +673,8 @@ power(Number f, unsigned n, unsigned d)
|
|||||||
return one;
|
return one;
|
||||||
if (abs(f) < one)
|
if (abs(f) < one)
|
||||||
return Number{};
|
return Number{};
|
||||||
if (abs(f) > one)
|
// abs(f) > one
|
||||||
throw std::overflow_error("Number::power infinity");
|
throw std::overflow_error("Number::power infinity");
|
||||||
throw std::overflow_error("Number::power nan");
|
|
||||||
}
|
}
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return one;
|
return one;
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#include <ripple/basics/Number.h>
|
#include <ripple/basics/Number.h>
|
||||||
#include <ripple/beast/unit_test.h>
|
#include <ripple/beast/unit_test.h>
|
||||||
#include <ripple/protocol/STAmount.h>
|
#include <ripple/protocol/STAmount.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -42,77 +44,267 @@ public:
|
|||||||
BEAST_EXPECT(z == -z);
|
BEAST_EXPECT(z == -z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_limits()
|
||||||
|
{
|
||||||
|
testcase("test_limits");
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Number x{10'000'000'000'000'000, 32768};
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
|
Number x{10'000'000'000'000'000, 32767};
|
||||||
|
BEAST_EXPECT((x == Number{1'000'000'000'000'000, 32768}));
|
||||||
|
Number z{1'000'000'000'000'000, -32769};
|
||||||
|
BEAST_EXPECT(z == Number{});
|
||||||
|
Number y{1'000'000'000'000'001'500, 32000};
|
||||||
|
BEAST_EXPECT((y == Number{1'000'000'000'000'002, 32003}));
|
||||||
|
Number m{std::numeric_limits<std::int64_t>::min()};
|
||||||
|
BEAST_EXPECT((m == Number{-9'223'372'036'854'776, 3}));
|
||||||
|
Number M{std::numeric_limits<std::int64_t>::max()};
|
||||||
|
BEAST_EXPECT((M == Number{9'223'372'036'854'776, 3}));
|
||||||
|
caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Number q{99'999'999'999'999'999, 32767};
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_add()
|
test_add()
|
||||||
{
|
{
|
||||||
testcase("test_add");
|
testcase("test_add");
|
||||||
Number x[]{
|
using Case = std::tuple<Number, Number, Number>;
|
||||||
Number{1'000'000'000'000'000, -15},
|
Case c[]{
|
||||||
Number{-1'000'000'000'000'000, -15},
|
{Number{1'000'000'000'000'000, -15},
|
||||||
Number{-1'000'000'000'000'000, -15},
|
|
||||||
Number{-6'555'555'555'555'555, -29}};
|
|
||||||
Number y[]{
|
|
||||||
Number{6'555'555'555'555'555, -29},
|
Number{6'555'555'555'555'555, -29},
|
||||||
|
Number{1'000'000'000'000'066, -15}},
|
||||||
|
{Number{-1'000'000'000'000'000, -15},
|
||||||
Number{-6'555'555'555'555'555, -29},
|
Number{-6'555'555'555'555'555, -29},
|
||||||
|
Number{-1'000'000'000'000'066, -15}},
|
||||||
|
{Number{-1'000'000'000'000'000, -15},
|
||||||
Number{6'555'555'555'555'555, -29},
|
Number{6'555'555'555'555'555, -29},
|
||||||
Number{1'000'000'000'000'000, -15}};
|
Number{-9'999'999'999'999'344, -16}},
|
||||||
Number z[]{
|
{Number{-6'555'555'555'555'555, -29},
|
||||||
Number{1'000'000'000'000'066, -15},
|
Number{1'000'000'000'000'000, -15},
|
||||||
Number{-1'000'000'000'000'066, -15},
|
Number{9'999'999'999'999'344, -16}},
|
||||||
Number{-9'999'999'999'999'344, -16},
|
{Number{}, Number{5}, Number{5}},
|
||||||
Number{9'999'999'999'999'344, -16}};
|
{Number{5'555'555'555'555'555, -32768},
|
||||||
for (unsigned i = 0; i < std::size(x); ++i)
|
Number{-5'555'555'555'555'554, -32768},
|
||||||
|
Number{0}},
|
||||||
|
{Number{-9'999'999'999'999'999, -31},
|
||||||
|
Number{1'000'000'000'000'000, -15},
|
||||||
|
Number{9'999'999'999'999'990, -16}}};
|
||||||
|
for (auto const& [x, y, z] : c)
|
||||||
|
BEAST_EXPECT(x + y == z);
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(x[i] + y[i] == z[i]);
|
Number{9'999'999'999'999'999, 32768} +
|
||||||
|
Number{5'000'000'000'000'000, 32767};
|
||||||
}
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_sub()
|
test_sub()
|
||||||
{
|
{
|
||||||
testcase("test_sub");
|
testcase("test_sub");
|
||||||
Number x[]{
|
using Case = std::tuple<Number, Number, Number>;
|
||||||
Number{1'000'000'000'000'000, -15},
|
Case c[]{
|
||||||
Number{6'555'555'555'555'555, -29}};
|
{Number{1'000'000'000'000'000, -15},
|
||||||
Number y[]{
|
|
||||||
Number{6'555'555'555'555'555, -29},
|
Number{6'555'555'555'555'555, -29},
|
||||||
Number{1'000'000'000'000'000, -15}};
|
Number{9'999'999'999'999'344, -16}},
|
||||||
Number z[]{
|
{Number{6'555'555'555'555'555, -29},
|
||||||
Number{9'999'999'999'999'344, -16},
|
Number{1'000'000'000'000'000, -15},
|
||||||
Number{-9'999'999'999'999'344, -16}};
|
Number{-9'999'999'999'999'344, -16}},
|
||||||
for (unsigned i = 0; i < std::size(x); ++i)
|
{Number{1'000'000'000'000'000, -15},
|
||||||
{
|
Number{1'000'000'000'000'000, -15},
|
||||||
BEAST_EXPECT(x[i] - y[i] == z[i]);
|
Number{0}},
|
||||||
|
{Number{1'000'000'000'000'000, -15},
|
||||||
|
Number{1'000'000'000'000'001, -15},
|
||||||
|
Number{-1'000'000'000'000'000, -30}},
|
||||||
|
{Number{1'000'000'000'000'001, -15},
|
||||||
|
Number{1'000'000'000'000'000, -15},
|
||||||
|
Number{1'000'000'000'000'000, -30}}};
|
||||||
|
for (auto const& [x, y, z] : c)
|
||||||
|
BEAST_EXPECT(x - y == z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_mul()
|
||||||
|
{
|
||||||
|
testcase("test_mul");
|
||||||
|
using Case = std::tuple<Number, Number, Number>;
|
||||||
|
Case c[]{
|
||||||
|
{Number{7}, Number{8}, Number{56}},
|
||||||
|
{Number{1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{2000000000000000, -15}},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{1414213562373095, -15},
|
||||||
|
Number{-2000000000000000, -15}},
|
||||||
|
{Number{-1414213562373095, -15},
|
||||||
|
Number{-1414213562373095, -15},
|
||||||
|
Number{2000000000000000, -15}},
|
||||||
|
{Number{3214285714285706, -15},
|
||||||
|
Number{3111111111111119, -15},
|
||||||
|
Number{1000000000000000, -14}},
|
||||||
|
{Number{1000000000000000, -32768},
|
||||||
|
Number{1000000000000000, -32768},
|
||||||
|
Number{0}}};
|
||||||
|
for (auto const& [x, y, z] : c)
|
||||||
|
BEAST_EXPECT(x * y == z);
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Number{9'999'999'999'999'999, 32768} *
|
||||||
|
Number{5'000'000'000'000'000, 32767};
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_div()
|
test_div()
|
||||||
{
|
{
|
||||||
testcase("test_div");
|
testcase("test_div");
|
||||||
Number x[]{Number{1}, Number{1}, Number{0}};
|
using Case = std::tuple<Number, Number, Number>;
|
||||||
Number y[]{Number{2}, Number{10}, Number{100}};
|
Case c[]{
|
||||||
Number z[]{Number{5, -1}, Number{1, -1}, Number{0}};
|
{Number{1}, Number{2}, Number{5, -1}},
|
||||||
for (unsigned i = 0; i < std::size(x); ++i)
|
{Number{1}, Number{10}, Number{1, -1}},
|
||||||
|
{Number{1}, Number{-10}, Number{-1, -1}},
|
||||||
|
{Number{0}, Number{100}, Number{0}}};
|
||||||
|
for (auto const& [x, y, z] : c)
|
||||||
|
BEAST_EXPECT(x / y == z);
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(x[i] / y[i] == z[i]);
|
Number{1000000000000000, -15} / Number{0};
|
||||||
}
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_root()
|
test_root()
|
||||||
{
|
{
|
||||||
testcase("test_root");
|
testcase("test_root");
|
||||||
Number x[]{Number{2}, Number{2'000'000}, Number{2, -30}};
|
using Case = std::tuple<Number, unsigned, Number>;
|
||||||
unsigned y[]{2, 2, 2};
|
Case c[]{
|
||||||
Number z[]{
|
{Number{2}, 2, Number{1414213562373095, -15}},
|
||||||
Number{1414213562373095, -15},
|
{Number{2'000'000}, 2, Number{1414213562373095, -12}},
|
||||||
Number{1414213562373095, -12},
|
{Number{2, -30}, 2, Number{1414213562373095, -30}},
|
||||||
Number{1414213562373095, -30}};
|
{Number{-27}, 3, Number{-3}},
|
||||||
for (unsigned i = 0; i < std::size(x); ++i)
|
{Number{1}, 5, Number{1}},
|
||||||
|
{Number{-1}, 0, Number{1}},
|
||||||
|
{Number{5, -1}, 0, Number{0}},
|
||||||
|
{Number{0}, 5, Number{0}},
|
||||||
|
{Number{5625, -4}, 2, Number{75, -2}}};
|
||||||
|
for (auto const& [x, y, z] : c)
|
||||||
|
BEAST_EXPECT((root(x, y) == z));
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(root(x[i], y[i]) == z[i]);
|
(void)root(Number{-2}, 0);
|
||||||
}
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
|
caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(void)root(Number{-2}, 4);
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_power1()
|
||||||
|
{
|
||||||
|
testcase("test_power1");
|
||||||
|
using Case = std::tuple<Number, unsigned, Number>;
|
||||||
|
Case c[]{
|
||||||
|
{Number{64}, 0, Number{1}},
|
||||||
|
{Number{64}, 1, Number{64}},
|
||||||
|
{Number{64}, 2, Number{4096}},
|
||||||
|
{Number{-64}, 2, Number{4096}},
|
||||||
|
{Number{64}, 3, Number{262144}},
|
||||||
|
{Number{-64}, 3, Number{-262144}}};
|
||||||
|
for (auto const& [x, y, z] : c)
|
||||||
|
BEAST_EXPECT((power(x, y) == z));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_power2()
|
||||||
|
{
|
||||||
|
testcase("test_power2");
|
||||||
|
using Case = std::tuple<Number, unsigned, unsigned, Number>;
|
||||||
|
Case c[]{
|
||||||
|
{Number{1}, 3, 7, Number{1}},
|
||||||
|
{Number{-1}, 1, 0, Number{1}},
|
||||||
|
{Number{-1, -1}, 1, 0, Number{0}},
|
||||||
|
{Number{16}, 0, 5, Number{1}},
|
||||||
|
{Number{34}, 3, 3, Number{34}},
|
||||||
|
{Number{4}, 3, 2, Number{8}}};
|
||||||
|
for (auto const& [x, n, d, z] : c)
|
||||||
|
BEAST_EXPECT((power(x, n, d) == z));
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(void)power(Number{7}, 0, 0);
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
|
caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(void)power(Number{7}, 1, 0);
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
|
caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(void)power(Number{-1, -1}, 3, 2);
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -129,102 +321,149 @@ public:
|
|||||||
STAmount st = xrp;
|
STAmount st = xrp;
|
||||||
Number n = st;
|
Number n = st;
|
||||||
BEAST_EXPECT(XRPAmount{n} == xrp);
|
BEAST_EXPECT(XRPAmount{n} == xrp);
|
||||||
|
IOUAmount x0{0, 0};
|
||||||
|
Number y0 = x0;
|
||||||
|
BEAST_EXPECT((y0 == Number{0}));
|
||||||
|
IOUAmount z0{y0};
|
||||||
|
BEAST_EXPECT(x0 == z0);
|
||||||
|
XRPAmount xrp0{0};
|
||||||
|
Number n0 = xrp0;
|
||||||
|
BEAST_EXPECT(n0 == Number{0});
|
||||||
|
XRPAmount xrp1{n0};
|
||||||
|
BEAST_EXPECT(xrp1 == xrp0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_to_integer()
|
test_to_integer()
|
||||||
{
|
{
|
||||||
testcase("test_to_integer");
|
testcase("test_to_integer");
|
||||||
Number x[]{
|
using Case = std::tuple<Number, std::int64_t>;
|
||||||
Number{0},
|
Case c[]{
|
||||||
Number{1},
|
{Number{0}, 0},
|
||||||
Number{2},
|
{Number{1}, 1},
|
||||||
Number{3},
|
{Number{2}, 2},
|
||||||
Number{-1},
|
{Number{3}, 3},
|
||||||
Number{-2},
|
{Number{-1}, -1},
|
||||||
Number{-3},
|
{Number{-2}, -2},
|
||||||
Number{10},
|
{Number{-3}, -3},
|
||||||
Number{99},
|
{Number{10}, 10},
|
||||||
Number{1155},
|
{Number{99}, 99},
|
||||||
Number{9'999'999'999'999'999, 0},
|
{Number{1155}, 1155},
|
||||||
Number{9'999'999'999'999'999, 1},
|
{Number{9'999'999'999'999'999, 0}, 9'999'999'999'999'999},
|
||||||
Number{9'999'999'999'999'999, 2},
|
{Number{9'999'999'999'999'999, 1}, 99'999'999'999'999'990},
|
||||||
Number{-9'999'999'999'999'999, 2},
|
{Number{9'999'999'999'999'999, 2}, 999'999'999'999'999'900},
|
||||||
Number{15, -1},
|
{Number{-9'999'999'999'999'999, 2}, -999'999'999'999'999'900},
|
||||||
Number{14, -1},
|
{Number{15, -1}, 2},
|
||||||
Number{16, -1},
|
{Number{14, -1}, 1},
|
||||||
Number{25, -1},
|
{Number{16, -1}, 2},
|
||||||
Number{6, -1},
|
{Number{25, -1}, 2},
|
||||||
Number{5, -1},
|
{Number{6, -1}, 1},
|
||||||
Number{4, -1},
|
{Number{5, -1}, 0},
|
||||||
Number{-15, -1},
|
{Number{4, -1}, 0},
|
||||||
Number{-14, -1},
|
{Number{-15, -1}, -2},
|
||||||
Number{-16, -1},
|
{Number{-14, -1}, -1},
|
||||||
Number{-25, -1},
|
{Number{-16, -1}, -2},
|
||||||
Number{-6, -1},
|
{Number{-25, -1}, -2},
|
||||||
Number{-5, -1},
|
{Number{-6, -1}, -1},
|
||||||
Number{-4, -1}};
|
{Number{-5, -1}, 0},
|
||||||
std::int64_t y[]{
|
{Number{-4, -1}, 0}};
|
||||||
0,
|
for (auto const& [x, y] : c)
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
-1,
|
|
||||||
-2,
|
|
||||||
-3,
|
|
||||||
10,
|
|
||||||
99,
|
|
||||||
1155,
|
|
||||||
9'999'999'999'999'999,
|
|
||||||
99'999'999'999'999'990,
|
|
||||||
999'999'999'999'999'900,
|
|
||||||
-999'999'999'999'999'900,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
-2,
|
|
||||||
-1,
|
|
||||||
-2,
|
|
||||||
-2,
|
|
||||||
-1,
|
|
||||||
0,
|
|
||||||
0};
|
|
||||||
static_assert(std::size(x) == std::size(y));
|
|
||||||
for (unsigned u = 0; u < std::size(x); ++u)
|
|
||||||
{
|
{
|
||||||
auto j = static_cast<std::int64_t>(x[u]);
|
auto j = static_cast<std::int64_t>(x);
|
||||||
BEAST_EXPECT(j == y[u]);
|
BEAST_EXPECT(j == y);
|
||||||
}
|
}
|
||||||
|
bool caught = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(void)static_cast<std::int64_t>(Number{9223372036854776, 3});
|
||||||
|
}
|
||||||
|
catch (std::overflow_error const&)
|
||||||
|
{
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_clip()
|
test_squelch()
|
||||||
{
|
{
|
||||||
testcase("test_clip");
|
testcase("test_squelch");
|
||||||
Number limit{1, -6};
|
Number limit{1, -6};
|
||||||
BEAST_EXPECT((clip(Number{2, -6}, limit) == Number{2, -6}));
|
BEAST_EXPECT((squelch(Number{2, -6}, limit) == Number{2, -6}));
|
||||||
BEAST_EXPECT((clip(Number{1, -6}, limit) == Number{1, -6}));
|
BEAST_EXPECT((squelch(Number{1, -6}, limit) == Number{1, -6}));
|
||||||
BEAST_EXPECT((clip(Number{9, -7}, limit) == Number{0}));
|
BEAST_EXPECT((squelch(Number{9, -7}, limit) == Number{0}));
|
||||||
BEAST_EXPECT((clip(Number{-2, -6}, limit) == Number{-2, -6}));
|
BEAST_EXPECT((squelch(Number{-2, -6}, limit) == Number{-2, -6}));
|
||||||
BEAST_EXPECT((clip(Number{-1, -6}, limit) == Number{-1, -6}));
|
BEAST_EXPECT((squelch(Number{-1, -6}, limit) == Number{-1, -6}));
|
||||||
BEAST_EXPECT((clip(Number{-9, -7}, limit) == Number{0}));
|
BEAST_EXPECT((squelch(Number{-9, -7}, limit) == Number{0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testToString()
|
||||||
|
{
|
||||||
|
testcase("testToString");
|
||||||
|
BEAST_EXPECT(to_string(Number(-2, 0)) == "-2");
|
||||||
|
BEAST_EXPECT(to_string(Number(0, 0)) == "0");
|
||||||
|
BEAST_EXPECT(to_string(Number(2, 0)) == "2");
|
||||||
|
BEAST_EXPECT(to_string(Number(25, -3)) == "0.025");
|
||||||
|
BEAST_EXPECT(to_string(Number(-25, -3)) == "-0.025");
|
||||||
|
BEAST_EXPECT(to_string(Number(25, 1)) == "250");
|
||||||
|
BEAST_EXPECT(to_string(Number(-25, 1)) == "-250");
|
||||||
|
BEAST_EXPECT(to_string(Number(2, 20)) == "2000000000000000e5");
|
||||||
|
BEAST_EXPECT(to_string(Number(-2, -20)) == "-2000000000000000e-35");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_relationals()
|
||||||
|
{
|
||||||
|
testcase("test_relationals");
|
||||||
|
BEAST_EXPECT(!(Number{100} < Number{10}));
|
||||||
|
BEAST_EXPECT(Number{100} > Number{10});
|
||||||
|
BEAST_EXPECT(Number{100} >= Number{10});
|
||||||
|
BEAST_EXPECT(!(Number{100} <= Number{10}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_stream()
|
||||||
|
{
|
||||||
|
testcase("test_stream");
|
||||||
|
Number x{100};
|
||||||
|
std::ostringstream os;
|
||||||
|
os << x;
|
||||||
|
BEAST_EXPECT(os.str() == to_string(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_inc_dec()
|
||||||
|
{
|
||||||
|
testcase("test_inc_dec");
|
||||||
|
Number x{100};
|
||||||
|
Number y = +x;
|
||||||
|
BEAST_EXPECT(x == y);
|
||||||
|
BEAST_EXPECT(x++ == y);
|
||||||
|
BEAST_EXPECT(x == Number{101});
|
||||||
|
BEAST_EXPECT(x-- == Number{101});
|
||||||
|
BEAST_EXPECT(x == y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testZero();
|
testZero();
|
||||||
|
test_limits();
|
||||||
test_add();
|
test_add();
|
||||||
test_sub();
|
test_sub();
|
||||||
|
test_mul();
|
||||||
test_div();
|
test_div();
|
||||||
test_root();
|
test_root();
|
||||||
|
test_power1();
|
||||||
|
test_power2();
|
||||||
testConversions();
|
testConversions();
|
||||||
test_to_integer();
|
test_to_integer();
|
||||||
test_clip();
|
test_squelch();
|
||||||
|
testToString();
|
||||||
|
test_relationals();
|
||||||
|
test_stream();
|
||||||
|
test_inc_dec();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user