#include #include #include #include #include #include #include namespace xrpl { class IOUAmount_test : public beast::unit_test::Suite { public: void testZero() { testcase("zero"); IOUAmount const z(0, 0); BEAST_EXPECT(z.mantissa() == 0); BEAST_EXPECT(z.exponent() == -100); BEAST_EXPECT(!z); BEAST_EXPECT(z.signum() == 0); BEAST_EXPECT(z == beast::kZERO); BEAST_EXPECT((z + z) == z); BEAST_EXPECT((z - z) == z); BEAST_EXPECT(z == -z); IOUAmount const zz(beast::kZERO); BEAST_EXPECT(z == zz); // https://github.com/XRPLF/rippled/issues/5170 IOUAmount const zzz{}; BEAST_EXPECT(zzz == beast::kZERO); // BEAST_EXPECT(zzz == zz); } void testSigNum() { testcase("signum"); IOUAmount const neg(-1, 0); BEAST_EXPECT(neg.signum() < 0); IOUAmount const zer(0, 0); BEAST_EXPECT(zer.signum() == 0); IOUAmount const pos(1, 0); BEAST_EXPECT(pos.signum() > 0); } void testBeastZero() { testcase("beast::Zero Comparisons"); using beast::kZERO; { IOUAmount const z(kZERO); BEAST_EXPECT(z == kZERO); BEAST_EXPECT(z >= kZERO); BEAST_EXPECT(z <= kZERO); unexpected(z != kZERO); unexpected(z > kZERO); unexpected(z < kZERO); } { IOUAmount const neg(-2, 0); BEAST_EXPECT(neg < kZERO); BEAST_EXPECT(neg <= kZERO); BEAST_EXPECT(neg != kZERO); unexpected(neg == kZERO); } { IOUAmount const pos(2, 0); BEAST_EXPECT(pos > kZERO); BEAST_EXPECT(pos >= kZERO); BEAST_EXPECT(pos != kZERO); unexpected(pos == kZERO); } } void testComparisons() { testcase("IOU Comparisons"); IOUAmount const n(-2, 0); IOUAmount const z(0, 0); IOUAmount const p(2, 0); BEAST_EXPECT(z == z); BEAST_EXPECT(z >= z); BEAST_EXPECT(z <= z); BEAST_EXPECT(z == -z); // NOLINTBEGIN(misc-redundant-expression) unexpected(z > z); unexpected(z < z); unexpected(z != z); // NOLINTEND(misc-redundant-expression) unexpected(z != -z); BEAST_EXPECT(n < z); BEAST_EXPECT(n <= z); BEAST_EXPECT(n != z); unexpected(n > z); unexpected(n >= z); unexpected(n == z); BEAST_EXPECT(p > z); BEAST_EXPECT(p >= z); BEAST_EXPECT(p != z); unexpected(p < z); unexpected(p <= z); unexpected(p == z); BEAST_EXPECT(n < p); BEAST_EXPECT(n <= p); BEAST_EXPECT(n != p); unexpected(n > p); unexpected(n >= p); unexpected(n == p); BEAST_EXPECT(p > n); BEAST_EXPECT(p >= n); BEAST_EXPECT(p != n); unexpected(p < n); unexpected(p <= n); unexpected(p == n); BEAST_EXPECT(p > -p); BEAST_EXPECT(p >= -p); BEAST_EXPECT(p != -p); BEAST_EXPECT(n < -n); BEAST_EXPECT(n <= -n); BEAST_EXPECT(n != -n); } void testToString() { testcase("IOU strings"); auto test = [this](IOUAmount const& n, std::string const& expected) { auto const result = to_string(n); std::stringstream ss; ss << "to_string(" << result << "). Expected: " << expected; BEAST_EXPECTS(result == expected, ss.str()); }; for (auto const mantissaSize : {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) { NumberMantissaScaleGuard const mg(mantissaSize); test(IOUAmount(-2, 0), "-2"); test(IOUAmount(0, 0), "0"); test(IOUAmount(2, 0), "2"); test(IOUAmount(25, -3), "0.025"); test(IOUAmount(-25, -3), "-0.025"); test(IOUAmount(25, 1), "250"); test(IOUAmount(-25, 1), "-250"); test(IOUAmount(2, 20), "2e20"); test(IOUAmount(-2, -20), "-2e-20"); } } void testMulRatio() { testcase("mulRatio"); /* The range for the mantissa when normalized */ constexpr std::int64_t kMIN_MANTISSA = 1000000000000000ull; constexpr std::int64_t kMAX_MANTISSA = 9999999999999999ull; // log(2,maxMantissa) ~ 53.15 /* The range for the exponent when normalized */ constexpr int kMIN_EXPONENT = -96; constexpr int kMAX_EXPONENT = 80; constexpr auto kMAX_U_INT = std::numeric_limits::max(); { // multiply by a number that would overflow the mantissa, then // divide by the same number, and check we didn't lose any value IOUAmount const bigMan(kMAX_MANTISSA, 0); BEAST_EXPECT(bigMan == mulRatio(bigMan, kMAX_U_INT, kMAX_U_INT, true)); // rounding mode shouldn't matter as the result is exact BEAST_EXPECT(bigMan == mulRatio(bigMan, kMAX_U_INT, kMAX_U_INT, false)); } { // Similar test as above, but for negative values IOUAmount const bigMan(-kMAX_MANTISSA, 0); BEAST_EXPECT(bigMan == mulRatio(bigMan, kMAX_U_INT, kMAX_U_INT, true)); // rounding mode shouldn't matter as the result is exact BEAST_EXPECT(bigMan == mulRatio(bigMan, kMAX_U_INT, kMAX_U_INT, false)); } { // small amounts IOUAmount const tiny(kMIN_MANTISSA, kMIN_EXPONENT); // Round up should give the smallest allowable number BEAST_EXPECT(tiny == mulRatio(tiny, 1, kMAX_U_INT, true)); BEAST_EXPECT(tiny == mulRatio(tiny, kMAX_U_INT - 1, kMAX_U_INT, true)); // rounding down should be zero BEAST_EXPECT(beast::kZERO == mulRatio(tiny, 1, kMAX_U_INT, false)); BEAST_EXPECT(beast::kZERO == mulRatio(tiny, kMAX_U_INT - 1, kMAX_U_INT, false)); // tiny negative numbers IOUAmount const tinyNeg(-kMIN_MANTISSA, kMIN_EXPONENT); // Round up should give zero BEAST_EXPECT(beast::kZERO == mulRatio(tinyNeg, 1, kMAX_U_INT, true)); BEAST_EXPECT(beast::kZERO == mulRatio(tinyNeg, kMAX_U_INT - 1, kMAX_U_INT, true)); // rounding down should be tiny BEAST_EXPECT(tinyNeg == mulRatio(tinyNeg, 1, kMAX_U_INT, false)); BEAST_EXPECT(tinyNeg == mulRatio(tinyNeg, kMAX_U_INT - 1, kMAX_U_INT, false)); } { // rounding { IOUAmount const one(1, 0); auto const rup = mulRatio(one, kMAX_U_INT - 1, kMAX_U_INT, true); auto const rdown = mulRatio(one, kMAX_U_INT - 1, kMAX_U_INT, false); BEAST_EXPECT(rup.mantissa() - rdown.mantissa() == 1); } { IOUAmount const big(kMAX_MANTISSA, kMAX_EXPONENT); auto const rup = mulRatio(big, kMAX_U_INT - 1, kMAX_U_INT, true); auto const rdown = mulRatio(big, kMAX_U_INT - 1, kMAX_U_INT, false); BEAST_EXPECT(rup.mantissa() - rdown.mantissa() == 1); } { IOUAmount const negOne(-1, 0); auto const rup = mulRatio(negOne, kMAX_U_INT - 1, kMAX_U_INT, true); auto const rdown = mulRatio(negOne, kMAX_U_INT - 1, kMAX_U_INT, false); BEAST_EXPECT(rup.mantissa() - rdown.mantissa() == 1); } } { // division by zero IOUAmount one(1, 0); except([&] { mulRatio(one, 1, 0, true); }); } { // overflow IOUAmount big(kMAX_MANTISSA, kMAX_EXPONENT); except([&] { mulRatio(big, 2, 0, true); }); } } // namespace xrpl //-------------------------------------------------------------------------- void run() override { testZero(); testSigNum(); testBeastZero(); testComparisons(); testToString(); testMulRatio(); } }; BEAST_DEFINE_TESTSUITE(IOUAmount, basics, xrpl); } // namespace xrpl