Add tests

This commit is contained in:
Howard Hinnant
2022-04-21 16:47:07 -04:00
committed by Elliot Lee
parent c9c54c9799
commit 48e804c40c
3 changed files with 447 additions and 142 deletions

View File

@@ -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 NewtonRaphson iterations until the result stops changing // Uses NewtonRaphson 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{};

View File

@@ -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 NewtonRaphson iterations until the result stops changing // Uses NewtonRaphson 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};
// NewtonRaphson 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;

View File

@@ -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{-6'555'555'555'555'555, -29}}; Number{1'000'000'000'000'066, -15}},
Number y[]{ {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{-6'555'555'555'555'555, -29}, Number{-1'000'000'000'000'066, -15}},
Number{6'555'555'555'555'555, -29}, {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 z[]{ Number{-9'999'999'999'999'344, -16}},
Number{1'000'000'000'000'066, -15}, {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{-9'999'999'999'999'344, -16}, Number{9'999'999'999'999'344, -16}},
Number{9'999'999'999'999'344, -16}}; {Number{}, Number{5}, Number{5}},
for (unsigned i = 0; i < std::size(x); ++i) {Number{5'555'555'555'555'555, -32768},
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{9'999'999'999'999'344, -16}},
Number{1'000'000'000'000'000, -15}}; {Number{6'555'555'555'555'555, -29},
Number z[]{ Number{1'000'000'000'000'000, -15},
Number{9'999'999'999'999'344, -16}, Number{-9'999'999'999'999'344, -16}},
Number{-9'999'999'999'999'344, -16}}; {Number{1'000'000'000'000'000, -15},
for (unsigned i = 0; i < std::size(x); ++i) Number{1'000'000'000'000'000, -15},
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
{ {
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_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();
} }
}; };