From 606e3ec0b7bb05e7fc2ea0843a694aebb5813ad9 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Fri, 14 Nov 2025 02:32:50 -0500 Subject: [PATCH] Step 2: Add the ability to change the mantissa range - Update tests. Unfinished. - TODO: Finish Number tests. Use both modes for STNumber tests. Move mantissa_scale into MantissaRange. --- include/xrpl/basics/Number.h | 32 +- src/libxrpl/basics/Number.cpp | 86 ++- src/test/basics/Number_test.cpp | 1066 +++++++++++++++++++++++-------- 3 files changed, 912 insertions(+), 272 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index b1a0bb7e8e..644b83b600 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -234,8 +234,11 @@ public: static rounding_mode setround(rounding_mode mode); + enum mantissa_scale { small, large }; + static mantissa_scale + getMantissaScale(); static void - setLargeMantissa(bool large); + setMantissaScale(mantissa_scale scale); inline static internalrep minMantissa() @@ -291,6 +294,7 @@ private: // The range for the mantissa when normalized. // Use reference_wrapper to avoid making copies, and prevent accidentally // changing the values inside the range. + static thread_local mantissa_scale scale_; static thread_local std::reference_wrapper range_; void @@ -536,6 +540,32 @@ public: operator=(NumberRoundModeGuard const&) = delete; }; +// Sets the new scale and restores the old scale when it leaves scope. Since +// Number doesn't have that facility, we'll build it here. +// +// This class may only end up needed in tests +class NumberMantissaScaleGuard +{ + Number::mantissa_scale saved_; + +public: + explicit NumberMantissaScaleGuard(Number::mantissa_scale scale) noexcept + : saved_{Number::getMantissaScale()} + { + Number::setMantissaScale(scale); + } + + ~NumberMantissaScaleGuard() + { + Number::setMantissaScale(saved_); + } + + NumberMantissaScaleGuard(NumberMantissaScaleGuard const&) = delete; + + NumberMantissaScaleGuard& + operator=(NumberMantissaScaleGuard const&) = delete; +}; + } // namespace ripple #endif // XRPL_BASICS_NUMBER_H_INCLUDED diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 13b2deca92..d6447df85c 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -36,6 +36,7 @@ namespace ripple { thread_local Number::rounding_mode Number::mode_ = Number::to_nearest; // TODO: Once the Rules switching is implemented, default to largeRange +thread_local Number::mantissa_scale Number::scale_ = small; // large; thread_local std::reference_wrapper Number::range_ = smallRange; // largeRange; @@ -51,6 +52,22 @@ Number::setround(rounding_mode mode) return std::exchange(mode_, mode); } +Number::mantissa_scale +Number::getMantissaScale() +{ + return scale_; +} + +void +Number::setMantissaScale(mantissa_scale scale) +{ + // scale_ and range_ MUST stay in lockstep + if (scale != mantissa_scale::small && scale != mantissa_scale::large) + LogicError("Unknown mantissa scale"); + scale_ = scale; + range_ = scale == mantissa_scale::small ? smallRange : largeRange; +} + // Guard // The Guard class is used to tempoarily add extra digits of @@ -524,14 +541,61 @@ Number::operator/=(Number const& y) // Shift by 10^17 gives greatest precision while not overflowing // uint128_t or the cast back to int64_t // TODO: Can/should this be made bigger for largeRange? - // 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); + // log(2^128,10) ~ 38.5 + // largeRange.log = 18, fits in 10^19 + // f can be up to 10^(38-19) = 10^19 safely + bool small = Number::scale_ == Number::small; + uint128_t const f = + small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000; + XRPL_ASSERT_PARTS( + f >= Number::minMantissa() * 10, + "Number::operator/=", + "factor expected size"); + + // unsigned denominator + uint128_t const dmu{dm}; + // correctionFactor can be anything between 10 and f, depending on how much + // extra precision we want to only use for rounding. + uint128_t const correctionFactor = 1'000; + + auto const numerator = uint128_t(nm) * f; + static_assert(smallRange.log == 15); - mantissa_ = uint128_t(nm) * f / uint128_t(dm); - exponent_ = ne - de - 17; + static_assert(largeRange.log == 18); + mantissa_ = numerator / dmu; + exponent_ = ne - de - (small ? 17 : 19); + if (!small) + { + // Virtually multiply numerator by correctionFactor. Since that would + // overflow, we'll do that part separately. + // The math for this would work for small mantissas, but we need to + // preserve existing behavior. + // + // Consider: + // ((numerator * correctionFactor) / dmu) / correctionFactor + // = ((numerator / dmu) * correctionFactor) / correctionFactor) + // + // But that assumes infinite precision. With integer math, this is + // equivalent to + // + // = ((numerator / dmu * correctionFactor) + // + ((numerator % dmu) * correctionFactor) / dmu) / correctionFactor + // + // We have already set `mantissa_ = numerator / dmu`. Now we + // compute `remainder = numerator % dmu`, and if it is + // nonzero, we do the rest of the arithmetic. If it's zero, we can skip + // it. + auto const remainder = (numerator % dmu); + if (remainder != 0) + { + mantissa_ *= correctionFactor; + auto const correction = remainder * correctionFactor / dmu; + mantissa_ += correction; + // divide by 1000 by moving the exponent, so we don't lose the + // integer value we just computed + exponent_ -= 3; + } + } mantissa_ *= np * dp; normalize(); return *this; @@ -617,8 +681,10 @@ to_string(Number const& amount) XRPL_ASSERT( exponent + 43 > 0, "ripple::to_string(Number) : minimum exponent"); - ptrdiff_t const pad_prefix = Number::mantissaLog() + 12; - ptrdiff_t const pad_suffix = Number::mantissaLog() + 8; + auto const mantissaLog = Number::mantissaLog(); + + ptrdiff_t const pad_prefix = mantissaLog + 12; + ptrdiff_t const pad_suffix = mantissaLog + 8; std::string const raw_value(std::to_string(mantissa)); std::string val; @@ -628,7 +694,7 @@ to_string(Number const& amount) val.append(raw_value); val.append(pad_suffix, '0'); - ptrdiff_t const offset(exponent + pad_prefix + 16); + ptrdiff_t const offset(exponent + pad_prefix + mantissaLog + 1); auto pre_from(val.begin()); auto const pre_to(val.begin() + offset); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 9380e9c5e1..a478a0ee63 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -9,13 +9,27 @@ namespace ripple { +std::string +to_string(Number::mantissa_scale const& scale) +{ + switch (scale) + { + case Number::small: + return "small"; + case Number::large: + return "large"; + default: + throw std::runtime_error("Bad scale"); + } +} + class Number_test : public beast::unit_test::suite { public: void testZero() { - testcase("zero"); + testcase << "zero " << to_string(Number::getMantissaScale()); for (Number const& z : {Number{0, 0}, Number{0}}) { @@ -31,31 +45,46 @@ public: void test_limits() { - testcase("test_limits"); + testcase << "test_limits " << to_string(Number::getMantissaScale()); bool caught = false; + auto const minMantissa = Number::minMantissa(); + auto const scale = Number::getMantissaScale(); try { - Number x{10'000'000'000'000'000, 32768}; + Number x{minMantissa * 10, 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}; + Number x{minMantissa * 10, 32767}; + BEAST_EXPECT((x == Number{minMantissa, 32768})); + Number z{minMantissa, -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 y{minMantissa * 1'000 + 1'500, 32000}; + BEAST_EXPECT((y == Number{minMantissa + 2, 32003})); Number m{std::numeric_limits::min()}; - BEAST_EXPECT((m == Number{-9'223'372'036'854'776, 3})); + // 9,223,372,036,854,775,808 + BEAST_EXPECT( + (m == + Number{ + scale == Number::small + ? -9'223'372'036'854'776 + : std::numeric_limits::min(), + 18 - Number::mantissaLog()})); Number M{std::numeric_limits::max()}; - BEAST_EXPECT((M == Number{9'223'372'036'854'776, 3})); + BEAST_EXPECT( + (M == + Number{ + scale == Number::small + ? 9'223'372'036'854'776 + : std::numeric_limits::max(), + 18 - Number::mantissaLog()})); caught = false; try { - Number q{99'999'999'999'999'999, 32767}; + Number q{minMantissa * 100 - 1, 32767}; } catch (std::overflow_error const&) { @@ -64,254 +93,759 @@ public: BEAST_EXPECT(caught); } + void + testToString() + { + testcase << "testToString " << to_string(Number::getMantissaScale()); + + auto const scale = Number::getMantissaScale(); + + 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"); + switch (scale) + { + case Number::small: + BEAST_EXPECT(to_string(Number(2, 20)) == "2000000000000000e5"); + BEAST_EXPECT( + to_string(Number(-2, -20)) == "-2000000000000000e-35"); + // Test the edges + // ((exponent < -(25)) || (exponent > -(5))))) + BEAST_EXPECT(to_string(Number(2, -10)) == "0.0000000002"); + BEAST_EXPECT( + to_string(Number(2, -11)) == "2000000000000000e-26"); + + BEAST_EXPECT(to_string(Number(-2, 10)) == "-20000000000"); + BEAST_EXPECT( + to_string(Number(-2, 11)) == "-2000000000000000e-4"); + + break; + case Number::large: + BEAST_EXPECT( + to_string(Number(2, 20)) == "2000000000000000000e2"); + BEAST_EXPECT( + to_string(Number(-2, -20)) == "-2000000000000000000e-38"); + // Test the edges + // ((exponent < -(28)) || (exponent > -(8))))) + BEAST_EXPECT(to_string(Number(2, -10)) == "0.0000000002"); + BEAST_EXPECT( + to_string(Number(2, -11)) == "2000000000000000000e-29"); + + BEAST_EXPECT(to_string(Number(-2, 10)) == "-20000000000"); + BEAST_EXPECT( + to_string(Number(-2, 11)) == "-2000000000000000000e-7"); + break; + default: + BEAST_EXPECT(false); + } + } + void test_add() { - testcase("test_add"); + testcase << "test_add " << to_string(Number::getMantissaScale()); + auto const minMantissa = Number::minMantissa(); + auto const scale = Number::getMantissaScale(); + using Case = std::tuple; - Case c[]{ - {Number{1'000'000'000'000'000, -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{-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{-9'999'999'999'999'344, -16}}, - {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{}, Number{5}, Number{5}}, - {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 + auto const cSmall = std::to_array( + {{Number{1'000'000'000'000'000, -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{-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{-9'999'999'999'999'344, -16}}, + {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{}, Number{5}, Number{5}}, + {Number{5}, Number{}, Number{5}}, + {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}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items from C + // with larger mantissa + {{Number{1'000'000'000'000'000, -15}, + Number{6'555'555'555'555'555, -29}, + Number{1'000'000'000'000'065'556, -18}}, + {Number{-1'000'000'000'000'000, -15}, + Number{-6'555'555'555'555'555, -29}, + Number{-1'000'000'000'000'065'556, -18}}, + {Number{-1'000'000'000'000'000, -15}, + Number{6'555'555'555'555'555, -29}, + Number{ + -(numberint128(9'999'999'999'999'344) * 1'000 + 444), -19}}, + {Number{-6'555'555'555'555'555, -29}, + Number{1'000'000'000'000'000, -15}, + Number{numberint128(9'999'999'999'999'344) * 1'000 + 444, -19}}, + {Number{}, Number{5}, Number{5}}, + {Number{5}, Number{}, Number{5}}, + {Number{5'555'555'555'555'555'000, -32768}, + Number{-5'555'555'555'555'554'000, -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}}, + // Items from cSmall expanded for the larger mantissa + {Number{1'000'000'000'000'000'000, -18}, + Number{6'555'555'555'555'555'555, -35}, + Number{1'000'000'000'000'000'066, -18}}, + {Number{-1'000'000'000'000'000'000, -18}, + Number{-6'555'555'555'555'555'555, -35}, + Number{-1'000'000'000'000'000'066, -18}}, + {Number{-1'000'000'000'000'000'000, -18}, + Number{6'555'555'555'555'555'555, -35}, + Number{ + -(numberint128(9'999'999'999'999'999) * 1'000 + 344), -19}}, + {Number{-6'555'555'555'555'555'555, -35}, + Number{1'000'000'000'000'000'000, -18}, + Number{numberint128(9'999'999'999'999'999) * 1'000 + 344, -19}}, + {Number{}, Number{5}, Number{5}}, + {Number{5'555'555'555'555'555'555, -32768}, + Number{-5'555'555'555'555'555'554, -32768}, + Number{0}}, + {Number{-(numberint128(9'999'999'999'999'999) * 1'000 + 999), -37}, + Number{1'000'000'000'000'000'000, -18}, + Number{numberint128(9'999'999'999'999'999) * 1'000 + 990, -19}}}); + auto test = [this](auto const& c) { + for (auto const& [x, y, z] : c) + { + auto const result = x + y; + std::stringstream ss; + ss << x << " + " << y << " = " << result << ". Expected: " << z; + BEAST_EXPECTS(result == z, ss.str()); + } + }; + if (scale == Number::small) + test(cSmall); + else + test(cLarge); { - Number{9'999'999'999'999'999, 32768} + - Number{5'000'000'000'000'000, 32767}; + bool caught = false; + try + { + if (scale == Number::small) + Number{9'999'999'999'999'999, 32768} + + Number{5'000'000'000'000'000, 32767}; + else + Number{numberint128(9'999'999'999'999'999) * 1'000, 32768} + + Number{5'000'000'000'000'000'000, 32767}; + } + catch (std::overflow_error const&) + { + caught = true; + } + BEAST_EXPECT(caught); } - catch (std::overflow_error const&) + if (scale != Number::small) { - caught = true; + bool caught = false; + try + { + Number{9'999'999'999'999'999'999, 32768} + + Number{5'000'000'000'000'000'000, 32767}; + } + catch (std::overflow_error const&) + { + caught = true; + } + BEAST_EXPECT(caught); } - BEAST_EXPECT(caught); } void test_sub() { - testcase("test_sub"); + testcase << "test_sub " << to_string(Number::getMantissaScale()); + auto const minMantissa = Number::minMantissa(); + auto const scale = Number::getMantissaScale(); + using Case = std::tuple; - Case c[]{ - {Number{1'000'000'000'000'000, -15}, - Number{6'555'555'555'555'555, -29}, - Number{9'999'999'999'999'344, -16}}, - {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{1'000'000'000'000'000, -15}, - 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); + auto const cSmall = std::to_array( + {{Number{1'000'000'000'000'000, -15}, + Number{6'555'555'555'555'555, -29}, + Number{9'999'999'999'999'344, -16}}, + {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{1'000'000'000'000'000, -15}, + 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}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items from C + // with larger mantissa + {{Number{1'000'000'000'000'000, -15}, + Number{6'555'555'555'555'555, -29}, + Number{numberint128(9'999'999'999'999'344) * 1'000 + 444, -19}}, + {Number{6'555'555'555'555'555, -29}, + Number{1'000'000'000'000'000, -15}, + Number{ + -(numberint128(9'999'999'999'999'344) * 1'000 + 444), -19}}, + {Number{1'000'000'000'000'000, -15}, + 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}}, + // Items from cSmall expanded for the larger mantissa + {Number{1'000'000'000'000'000'000, -18}, + Number{6'555'555'555'555'555'555, -32}, + Number{numberint128(9'999'999'999'999'344) * 1'000 + 444, -19}}, + {Number{6'555'555'555'555'555'555, -32}, + Number{1'000'000'000'000'000'000, -18}, + Number{ + -(numberint128(9'999'999'999'999'344) * 1'000 + 444), -19}}, + {Number{1'000'000'000'000'000'000, -18}, + Number{1'000'000'000'000'000'000, -18}, + Number{0}}, + {Number{1'000'000'000'000'000'000, -18}, + Number{1'000'000'000'000'000'001, -18}, + Number{-1'000'000'000'000'000'000, -36}}, + {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, -36}}}); + auto test = [this](auto const& c) { + for (auto const& [x, y, z] : c) + { + auto const result = x - y; + std::stringstream ss; + ss << x << " - " << y << " = " << result << ". Expected: " << z; + BEAST_EXPECTS(result == z, ss.str()); + } + }; + if (scale == Number::small) + test(cSmall); + else + test(cLarge); } void test_mul() { - testcase("test_mul"); + testcase << "test_mul " << to_string(Number::getMantissaScale()); + auto const scale = Number::getMantissaScale(); + using Case = std::tuple; + auto test = [this](auto const& c) { + for (auto const& [x, y, z] : c) + { + auto const result = x * y; + std::stringstream ss; + ss << x << " * " << y << " = " << result << ". Expected: " << z; + BEAST_EXPECTS(result == z, ss.str()); + } + }; + auto tests = [&](auto const& cSmall, auto const& cLarge) { + if (scale == Number::small) + test(cSmall); + else + test(cLarge); + }; saveNumberRoundMode save{Number::setround(Number::to_nearest)}; { - 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); + auto const cSmall = std::to_array( + {{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}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999579, -18}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + // Sadly, it looks like sqrt(2)^2 != 2 with higher precision + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2000000000000000001, -18}}, + {Number{-1414213562373095048, -18}, + Number{1414213562373095048, -18}, + Number{-1999999999999999998, -18}}, + {Number{-1414213562373095048, -18}, + Number{-1414213562373095049, -18}, + Number{1999999999999999999, -18}}, + {Number{3214285714285714278, -18}, + Number{3111111111111111119, -18}, + Number{10, 0}}}); + tests(cSmall, cLarge); } Number::setround(Number::towards_zero); + testcase << "test_mul " << to_string(Number::getMantissaScale()) + << " towards_zero"; { - Case c[]{ - {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, - Number{1414213562373095, -15}, - Number{1999999999999999, -15}}, - {Number{-1414213562373095, -15}, - Number{1414213562373095, -15}, - Number{-1999999999999999, -15}}, - {Number{-1414213562373095, -15}, - Number{-1414213562373095, -15}, - Number{1999999999999999, -15}}, - {Number{3214285714285706, -15}, - Number{3111111111111119, -15}, - Number{9999999999999999, -15}}, - {Number{1000000000000000, -32768}, - Number{1000000000000000, -32768}, - Number{0}}}; - for (auto const& [x, y, z] : c) - BEAST_EXPECT(x * y == z); + auto const cSmall = std::to_array( + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999, -15}}, + {Number{1000000000000000, -32768}, + Number{1000000000000000, -32768}, + Number{0}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999861, -18}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999579, -18}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + // Sadly, it looks like sqrt(2)^2 != 2 with higher precision + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2, 0}}, + {Number{-1414213562373095048, -18}, + Number{1414213562373095048, -18}, + Number{-1999999999999999997, -18}}, + {Number{-1414213562373095048, -18}, + Number{-1414213562373095049, -18}, + Number{1999999999999999999, -18}}, + {Number{3214285714285714278, -18}, + Number{3111111111111111119, -18}, + Number{10, 0}}}); + tests(cSmall, cLarge); } Number::setround(Number::downward); + testcase << "test_mul " << to_string(Number::getMantissaScale()) + << " downward"; { - Case c[]{ - {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, - Number{1414213562373095, -15}, - Number{1999999999999999, -15}}, - {Number{-1414213562373095, -15}, - Number{1414213562373095, -15}, - Number{-2000000000000000, -15}}, - {Number{-1414213562373095, -15}, - Number{-1414213562373095, -15}, - Number{1999999999999999, -15}}, - {Number{3214285714285706, -15}, - Number{3111111111111119, -15}, - Number{9999999999999999, -15}}, - {Number{1000000000000000, -32768}, - Number{1000000000000000, -32768}, - Number{0}}}; - for (auto const& [x, y, z] : c) - BEAST_EXPECT(x * y == z); + auto const cSmall = std::to_array( + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-2000000000000000, -15}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999, -15}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999, -15}}, + {Number{1000000000000000, -32768}, + Number{1000000000000000, -32768}, + Number{0}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999861, -18}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{9999999999999999579, -18}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + // Sadly, it looks like sqrt(2)^2 != 2 with higher precision + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2, 0}}, + {Number{-1414213562373095048, -18}, + Number{1414213562373095048, -18}, + Number{-1999999999999999998, -18}}, + {Number{-1414213562373095048, -18}, + Number{-1414213562373095049, -18}, + Number{1999999999999999999, -18}}, + {Number{3214285714285714278, -18}, + Number{3111111111111111119, -18}, + Number{10, 0}}}); + tests(cSmall, cLarge); } Number::setround(Number::upward); + testcase << "test_mul " << to_string(Number::getMantissaScale()) + << " upward"; { - Case c[]{ - {Number{7}, Number{8}, Number{56}}, - {Number{1414213562373095, -15}, - Number{1414213562373095, -15}, - Number{2000000000000000, -15}}, - {Number{-1414213562373095, -15}, - Number{1414213562373095, -15}, - Number{-1999999999999999, -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); + auto const cSmall = std::to_array( + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{2000000000000000, -15}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999, -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}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{7}, Number{8}, Number{56}}, + {Number{1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{-1414213562373095, -15}, + Number{1414213562373095, -15}, + Number{-1999999999999999861, -18}}, + {Number{-1414213562373095, -15}, + Number{-1414213562373095, -15}, + Number{1999999999999999862, -18}}, + {Number{3214285714285706, -15}, + Number{3111111111111119, -15}, + Number{999999999999999958, -17}}, + {Number{1000000000000000000, -32768}, + Number{1000000000000000000, -32768}, + Number{0}}, + // Items from cSmall expanded for the larger mantissa, + // except duplicates. Sadly, it looks like sqrt(2)^2 != 2 + // with higher precision + {Number{1414213562373095049, -18}, + Number{1414213562373095049, -18}, + Number{2000000000000000001, -18}}, + {Number{-1414213562373095048, -18}, + Number{1414213562373095048, -18}, + Number{-1999999999999999997, -18}}, + {Number{-1414213562373095048, -18}, + Number{-1414213562373095049, -18}, + Number{2, 0}}, + {Number{3214285714285714278, -18}, + Number{3111111111111111119, -18}, + Number{1000000000000000001, -17}}}); + tests(cSmall, cLarge); } - bool caught = false; - try + testcase << "test_mul " << to_string(Number::getMantissaScale()) + << " overflow"; { - Number{9'999'999'999'999'999, 32768} * - Number{5'000'000'000'000'000, 32767}; + bool caught = false; + try + { + if (scale == Number::small) + Number{9'999'999'999'999'999, 32768} * + Number{5'000'000'000'000'000, 32767}; + else + Number{numberint128(9'999'999'999'999'999) * 1'000, 32768} * + Number{5'000'000'000'000'000'000, 32767}; + } + catch (std::overflow_error const&) + { + caught = true; + } + BEAST_EXPECT(caught); } - catch (std::overflow_error const&) + if (scale != Number::small) { - caught = true; + bool caught = false; + try + { + Number{9'999'999'999'999'999'999, 32768} + + Number{5'000'000'000'000'000'000, 32767}; + } + catch (std::overflow_error const&) + { + caught = true; + } + BEAST_EXPECT(caught); } - BEAST_EXPECT(caught); } void test_div() { - testcase("test_div"); + testcase << "test_div " << to_string(Number::getMantissaScale()); + auto const scale = Number::getMantissaScale(); + using Case = std::tuple; + auto test = [this](auto const& c) { + for (auto const& [x, y, z] : c) + { + auto const result = x / y; + std::stringstream ss; + ss << x << " / " << y << " = " << result << ". Expected: " << z; + BEAST_EXPECTS(result == z, ss.str()); + } + }; + auto tests = [&](auto const& cSmall, auto const& cLarge) { + if (scale == Number::small) + test(cSmall); + else + test(cLarge); + }; saveNumberRoundMode save{Number::setround(Number::to_nearest)}; { - Case c[]{ - {Number{1}, Number{2}, Number{5, -1}}, - {Number{1}, Number{10}, Number{1, -1}}, - {Number{1}, Number{-10}, Number{-1, -1}}, - {Number{0}, Number{100}, Number{0}}, - {Number{1414213562373095, -10}, - Number{1414213562373095, -10}, - Number{1}}, - {Number{9'999'999'999'999'999}, - Number{1'000'000'000'000'000}, - Number{9'999'999'999'999'999, -15}}, - {Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}}, - {Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}}; - for (auto const& [x, y, z] : c) - BEAST_EXPECT(x / y == z); + auto const cSmall = std::to_array( + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}}, + {Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428, -16}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'666'667, -19}}, + {Number{-2}, + Number{3}, + Number{-6'666'666'666'666'666'667, -19}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428'571, -19}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + {Number{1414213562373095049, -13}, + Number{1414213562373095049, -13}, + Number{1}}, + {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + Number{1'000'000'000'000'000'000}, + Number{ + numberint128(9'999'999'999'999'999) * 1'000 + 999, + -18}}}); + tests(cSmall, cLarge); } + testcase << "test_div " << to_string(Number::getMantissaScale()) + << " towards_zero"; Number::setround(Number::towards_zero); { - Case c[]{ - {Number{1}, Number{2}, Number{5, -1}}, - {Number{1}, Number{10}, Number{1, -1}}, - {Number{1}, Number{-10}, Number{-1, -1}}, - {Number{0}, Number{100}, Number{0}}, - {Number{1414213562373095, -10}, - Number{1414213562373095, -10}, - Number{1}}, - {Number{9'999'999'999'999'999}, - Number{1'000'000'000'000'000}, - Number{9'999'999'999'999'999, -15}}, - {Number{2}, Number{3}, Number{6'666'666'666'666'666, -16}}, - {Number{-2}, Number{3}, Number{-6'666'666'666'666'666, -16}}}; - for (auto const& [x, y, z] : c) - BEAST_EXPECT(x / y == z); + auto const cSmall = std::to_array( + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'666, -16}}, + {Number{-2}, Number{3}, Number{-6'666'666'666'666'666, -16}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428, -16}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'666'666, -19}}, + {Number{-2}, + Number{3}, + Number{-6'666'666'666'666'666'666, -19}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428'571, -19}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + {Number{1414213562373095049, -13}, + Number{1414213562373095049, -13}, + Number{1}}, + {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + Number{1'000'000'000'000'000'000}, + Number{ + numberint128(9'999'999'999'999'999) * 1'000 + 999, + -18}}}); + tests(cSmall, cLarge); } + testcase << "test_div " << to_string(Number::getMantissaScale()) + << " downward"; Number::setround(Number::downward); { - Case c[]{ - {Number{1}, Number{2}, Number{5, -1}}, - {Number{1}, Number{10}, Number{1, -1}}, - {Number{1}, Number{-10}, Number{-1, -1}}, - {Number{0}, Number{100}, Number{0}}, - {Number{1414213562373095, -10}, - Number{1414213562373095, -10}, - Number{1}}, - {Number{9'999'999'999'999'999}, - Number{1'000'000'000'000'000}, - Number{9'999'999'999'999'999, -15}}, - {Number{2}, Number{3}, Number{6'666'666'666'666'666, -16}}, - {Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}}; - for (auto const& [x, y, z] : c) - BEAST_EXPECT(x / y == z); + auto const cSmall = std::to_array( + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'666, -16}}, + {Number{-2}, Number{3}, Number{-6'666'666'666'666'667, -16}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428, -16}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'666'666, -19}}, + {Number{-2}, + Number{3}, + Number{-6'666'666'666'666'666'667, -19}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428'571, -19}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + {Number{1414213562373095049, -13}, + Number{1414213562373095049, -13}, + Number{1}}, + {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + Number{1'000'000'000'000'000'000}, + Number{ + numberint128(9'999'999'999'999'999) * 1'000 + 999, + -18}}}); + tests(cSmall, cLarge); } + testcase << "test_div " << to_string(Number::getMantissaScale()) + << " upward"; Number::setround(Number::upward); { - Case c[]{ - {Number{1}, Number{2}, Number{5, -1}}, - {Number{1}, Number{10}, Number{1, -1}}, - {Number{1}, Number{-10}, Number{-1, -1}}, - {Number{0}, Number{100}, Number{0}}, - {Number{1414213562373095, -10}, - Number{1414213562373095, -10}, - Number{1}}, - {Number{9'999'999'999'999'999}, - Number{1'000'000'000'000'000}, - Number{9'999'999'999'999'999, -15}}, - {Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}}, - {Number{-2}, Number{3}, Number{-6'666'666'666'666'666, -16}}}; - for (auto const& [x, y, z] : c) - BEAST_EXPECT(x / y == z); + auto const cSmall = std::to_array( + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'667, -16}}, + {Number{-2}, Number{3}, Number{-6'666'666'666'666'666, -16}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'429, -16}}}); + auto const cLarge = std::to_array( + // Note that items with extremely large mantissas need to be + // calculated, because otherwise they overflow uint64. Items + // from C with larger mantissa + {{Number{1}, Number{2}, Number{5, -1}}, + {Number{1}, Number{10}, Number{1, -1}}, + {Number{1}, Number{-10}, Number{-1, -1}}, + {Number{0}, Number{100}, Number{0}}, + {Number{1414213562373095, -10}, + Number{1414213562373095, -10}, + Number{1}}, + {Number{9'999'999'999'999'999}, + Number{1'000'000'000'000'000}, + Number{9'999'999'999'999'999, -15}}, + {Number{2}, Number{3}, Number{6'666'666'666'666'666'667, -19}}, + {Number{-2}, + Number{3}, + Number{-6'666'666'666'666'666'666, -19}}, + {Number{1}, Number{7}, Number{1'428'571'428'571'428'572, -19}}, + // Items from cSmall expanded for the larger mantissa, except + // duplicates. + {Number{1414213562373095049, -13}, + Number{1414213562373095049, -13}, + Number{1}}, + {Number{numberint128(9'999'999'999'999'999) * 1'000 + 999, 0}, + Number{1'000'000'000'000'000'000}, + Number{ + numberint128(9'999'999'999'999'999) * 1'000 + 999, + -18}}}); + tests(cSmall, cLarge); } + testcase << "test_div " << to_string(Number::getMantissaScale()) + << " overflow"; bool caught = false; try { @@ -327,20 +861,40 @@ public: void test_root() { - testcase("test_root"); + testcase << "test_root " << to_string(Number::getMantissaScale()); + auto const scale = Number::getMantissaScale(); + using Case = std::tuple; - Case c[]{ - {Number{2}, 2, Number{1414213562373095, -15}}, - {Number{2'000'000}, 2, Number{1414213562373095, -12}}, - {Number{2, -30}, 2, Number{1414213562373095, -30}}, - {Number{-27}, 3, Number{-3}}, - {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)); + auto test = [this](auto const& c) { + for (auto const& [x, y, z] : c) + { + auto const result = root(x, y); + std::stringstream ss; + ss << "root(" << x << ", " << y << ") = " << result + << ". Expected: " << z; + log << ss.str() << std::endl; + BEAST_EXPECTS(result == z, ss.str()); + } + }; + /* + auto tests = [&](auto const& cSmall, auto const& cLarge) { + test(cSmall); + if (scale != Number::small) + test(cLarge); + }; + */ + + auto const cSmall = std::to_array( + {{Number{2}, 2, Number{1414213562373095049, -15}}, + {Number{2'000'000}, 2, Number{1414213562373095, -12}}, + {Number{2, -30}, 2, Number{1414213562373095, -30}}, + {Number{-27}, 3, Number{-3}}, + {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}}}); + test(cSmall); bool caught = false; try { @@ -366,7 +920,7 @@ public: void test_power1() { - testcase("test_power1"); + testcase << "test_power1 " << to_string(Number::getMantissaScale()); using Case = std::tuple; Case c[]{ {Number{64}, 0, Number{1}}, @@ -382,7 +936,7 @@ public: void test_power2() { - testcase("test_power2"); + testcase << "test_power2 " << to_string(Number::getMantissaScale()); using Case = std::tuple; Case c[]{ {Number{1}, 3, 7, Number{1}}, @@ -428,7 +982,7 @@ public: void testConversions() { - testcase("testConversions"); + testcase << "testConversions " << to_string(Number::getMantissaScale()); IOUAmount x{5, 6}; Number y = x; @@ -454,7 +1008,7 @@ public: void test_to_integer() { - testcase("test_to_integer"); + testcase << "test_to_integer " << to_string(Number::getMantissaScale()); using Case = std::tuple; saveNumberRoundMode save{Number::setround(Number::to_nearest)}; { @@ -622,7 +1176,7 @@ public: void test_squelch() { - testcase("test_squelch"); + testcase << "test_squelch " << to_string(Number::getMantissaScale()); Number limit{1, -6}; BEAST_EXPECT((squelch(Number{2, -6}, limit) == Number{2, -6})); BEAST_EXPECT((squelch(Number{1, -6}, limit) == Number{1, -6})); @@ -632,25 +1186,11 @@ public: 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"); + testcase << "test_relationals " + << to_string(Number::getMantissaScale()); BEAST_EXPECT(!(Number{100} < Number{10})); BEAST_EXPECT(Number{100} > Number{10}); BEAST_EXPECT(Number{100} >= Number{10}); @@ -660,7 +1200,7 @@ public: void test_stream() { - testcase("test_stream"); + testcase << "test_stream " << to_string(Number::getMantissaScale()); Number x{100}; std::ostringstream os; os << x; @@ -670,7 +1210,7 @@ public: void test_inc_dec() { - testcase("test_inc_dec"); + testcase << "test_inc_dec " << to_string(Number::getMantissaScale()); Number x{100}; Number y = +x; BEAST_EXPECT(x == y); @@ -730,7 +1270,7 @@ public: void testInt64() { - testcase("std::int64_t"); + testcase << "std::int64_t " << to_string(Number::getMantissaScale()); // Control case BEAST_EXPECT(Number::maxMantissa() > 10); @@ -759,25 +1299,29 @@ public: void run() override { - testZero(); - test_limits(); - test_add(); - test_sub(); - test_mul(); - test_div(); - test_root(); - test_power1(); - test_power2(); - testConversions(); - test_to_integer(); - test_squelch(); - testToString(); - test_relationals(); - test_stream(); - test_inc_dec(); - test_toSTAmount(); - test_truncate(); - testInt64(); + for (auto const scale : {Number::small, Number::large}) + { + NumberMantissaScaleGuard sg(scale); + testZero(); + test_limits(); + testToString(); + test_add(); + test_sub(); + test_mul(); + test_div(); + test_root(); + test_power1(); + test_power2(); + testConversions(); + test_to_integer(); + test_squelch(); + test_relationals(); + test_stream(); + test_inc_dec(); + test_toSTAmount(); + test_truncate(); + testInt64(); + } } };