diff --git a/src/ripple/basics/impl/IOUAmount.cpp b/src/ripple/basics/impl/IOUAmount.cpp index 1fa5e4fd2..76f4bbe9f 100644 --- a/src/ripple/basics/impl/IOUAmount.cpp +++ b/src/ripple/basics/impl/IOUAmount.cpp @@ -45,6 +45,12 @@ IOUAmount::minPositiveAmount() void IOUAmount::normalize() { + if (mantissa_ == 0) + { + *this = beast::zero; + return; + } + if (*stNumberSwitchover) { Number v{mantissa_, exponent_}; @@ -56,11 +62,6 @@ IOUAmount::normalize() *this = beast::zero; return; } - if (mantissa_ == 0) - { - *this = beast::zero; - return; - } bool const negative = (mantissa_ < 0); @@ -107,48 +108,46 @@ IOUAmount::IOUAmount(Number const& other) IOUAmount& IOUAmount::operator+=(IOUAmount const& other) { + if (other == beast::zero) + return *this; + + if (*this == beast::zero) + { + *this = other; + return *this; + } + if (*stNumberSwitchover) { *this = IOUAmount{Number{*this} + Number{other}}; + return *this; } - else + auto m = other.mantissa_; + auto e = other.exponent_; + + while (exponent_ < e) { - if (other == beast::zero) - return *this; - - if (*this == beast::zero) - { - *this = other; - return *this; - } - - auto m = other.mantissa_; - auto e = other.exponent_; - - while (exponent_ < e) - { - mantissa_ /= 10; - ++exponent_; - } - - while (e < exponent_) - { - m /= 10; - ++e; - } - - // This addition cannot overflow an std::int64_t but we may throw from - // normalize if the result isn't representable. - mantissa_ += m; - - if (mantissa_ >= -10 && mantissa_ <= 10) - { - *this = beast::zero; - return *this; - } - - normalize(); + mantissa_ /= 10; + ++exponent_; } + + while (e < exponent_) + { + m /= 10; + ++e; + } + + // This addition cannot overflow an std::int64_t but we may throw from + // normalize if the result isn't representable. + mantissa_ += m; + + if (mantissa_ >= -10 && mantissa_ <= 10) + { + *this = beast::zero; + return *this; + } + + normalize(); return *this; } diff --git a/src/ripple/basics/impl/Number.cpp b/src/ripple/basics/impl/Number.cpp index c4b6daffa..a7a8159fe 100644 --- a/src/ripple/basics/impl/Number.cpp +++ b/src/ripple/basics/impl/Number.cpp @@ -25,7 +25,7 @@ #include #include -#ifdef _MSVC_LANG +#ifdef BOOST_COMP_MSVC #include using uint128_t = boost::multiprecision::uint128_t; #else // !defined(_MSVC_LANG) @@ -130,34 +130,37 @@ int Number::Guard::round() noexcept { auto mode = Number::getround(); - switch (mode) + + if (mode == towards_zero) + return -1; + + if (mode == downward) { - // round to nearest if mode is not one of the predefined values - default: - case to_nearest: - if (digits_ > 0x5000'0000'0000'0000) - return 1; - if (digits_ < 0x5000'0000'0000'0000) - return -1; - if (xbit_) - return 1; - return 0; - case towards_zero: - return -1; - case downward: - if (sbit_) - { - if (digits_ > 0 || xbit_) - return 1; - } - return -1; - case upward: - if (sbit_) - return -1; + if (sbit_) + { if (digits_ > 0 || xbit_) return 1; - return -1; + } + return -1; } + + if (mode == upward) + { + if (sbit_) + return -1; + if (digits_ > 0 || xbit_) + return 1; + return -1; + } + + // assume round to nearest if mode is not one of the predefined values + if (digits_ > 0x5000'0000'0000'0000) + return 1; + if (digits_ < 0x5000'0000'0000'0000) + return -1; + if (xbit_) + return 1; + return 0; } // Number @@ -173,9 +176,9 @@ Number::normalize() return; } bool const negative = (mantissa_ < 0); - if (negative) - mantissa_ = -mantissa_; auto m = static_cast>(mantissa_); + if (negative) + m = -m; while ((m < minMantissa) && (exponent_ > minExponent)) { m *= 10; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index fc9a38cd2..1162612b7 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -2092,37 +2092,53 @@ public: using namespace jtx; - Env env{*this, features}; + for (auto NumberSwitchOver : {false, true}) + { + Env env{*this, features}; + if (NumberSwitchOver) + env.enableFeature(fixUniversalNumber); + else + env.disableFeature(fixUniversalNumber); - auto const gw = Account{"gateway"}; - auto const alice = Account{"alice"}; - auto const bob = Account{"bob"}; - auto const USD = gw["USD"]; + auto const gw = Account{"gateway"}; + auto const alice = Account{"alice"}; + auto const bob = Account{"bob"}; + auto const USD = gw["USD"]; - env.fund(XRP(10000), gw, alice, bob); + env.fund(XRP(10000), gw, alice, bob); - env(rate(gw, 1.005)); + env(rate(gw, 1.005)); - env(trust(alice, USD(1000))); - env(trust(bob, USD(1000))); - env(trust(gw, alice["USD"](50))); + env(trust(alice, USD(1000))); + env(trust(bob, USD(1000))); + env(trust(gw, alice["USD"](50))); - env(pay(gw, bob, bob["USD"](1))); - env(pay(alice, gw, USD(50))); + env(pay(gw, bob, bob["USD"](1))); + env(pay(alice, gw, USD(50))); - env(trust(gw, alice["USD"](0))); + env(trust(gw, alice["USD"](0))); - env(offer(alice, USD(50), XRP(150000))); - env(offer(bob, XRP(100), USD(0.1))); + env(offer(alice, USD(50), XRP(150000))); + env(offer(bob, XRP(100), USD(0.1))); - auto jrr = ledgerEntryState(env, alice, gw, "USD"); - BEAST_EXPECT( - jrr[jss::node][sfBalance.fieldName][jss::value] == - "49.96666666666667"); - jrr = ledgerEntryState(env, bob, gw, "USD"); - BEAST_EXPECT( - jrr[jss::node][sfBalance.fieldName][jss::value] == - "-0.9665000000333333"); + auto jrr = ledgerEntryState(env, alice, gw, "USD"); + BEAST_EXPECT( + jrr[jss::node][sfBalance.fieldName][jss::value] == + "49.96666666666667"); + jrr = ledgerEntryState(env, bob, gw, "USD"); + if (NumberSwitchOver) + { + BEAST_EXPECT( + jrr[jss::node][sfBalance.fieldName][jss::value] == + "-0.9665000000333333"); + } + else + { + BEAST_EXPECT( + jrr[jss::node][sfBalance.fieldName][jss::value] == + "-0.966500000033334"); + } + } } void diff --git a/src/test/app/Taker_test.cpp b/src/test/app/Taker_test.cpp index c7474b679..38735d8cf 100644 --- a/src/test/app/Taker_test.cpp +++ b/src/test/app/Taker_test.cpp @@ -905,100 +905,143 @@ public: { testcase("IOU to IOU"); - NumberSO stNumberSO{true}; - Quality q1 = get_quality("1", "1"); + for (auto NumberSwitchOver : {false, true}) + { + NumberSO stNumberSO{NumberSwitchOver}; + Quality q1 = get_quality("1", "1"); - // Highly exaggerated 50% transfer rate for the input and output: - Rate const rate{parityRate.value + (parityRate.value / 2)}; + // Highly exaggerated 50% transfer rate for the input and output: + Rate const rate{parityRate.value + (parityRate.value / 2)}; - // TAKER OWNER - // QUAL OFFER FUNDS QUAL OFFER FUNDS - // EXPECTED - // EUR USD - attempt( - Sell, - "N:N", - q1, - {"2", "2"}, - "10", - q1, - {"2", "2"}, - "10", - {"2", "2"}, - eur(), - usd(), - rate, - rate); - attempt( - Sell, - "N:B", - q1, - {"4", "4"}, - "10", - q1, - {"4", "4"}, - "4", - {"2.666666666666667", "2.666666666666667"}, - eur(), - usd(), - rate, - rate); - attempt( - Buy, - "N:T", - q1, - {"1", "1"}, - "10", - q1, - {"2", "2"}, - "10", - {"1", "1"}, - eur(), - usd(), - rate, - rate); - attempt( - Buy, - "N:BT", - q1, - {"2", "2"}, - "10", - q1, - {"6", "6"}, - "5", - {"2", "2"}, - eur(), - usd(), - rate, - rate); - attempt( - Buy, - "N:TB", - q1, - {"2", "2"}, - "2", - q1, - {"6", "6"}, - "1", - {"0.6666666666666667", "0.6666666666666667"}, - eur(), - usd(), - rate, - rate); - attempt( - Sell, - "A:N", - q1, - {"2", "2"}, - "2.5", - q1, - {"2", "2"}, - "10", - {"1.666666666666667", "1.666666666666667"}, - eur(), - usd(), - rate, - rate); + // TAKER OWNER + // QUAL OFFER FUNDS QUAL OFFER FUNDS + // EXPECTED + // EUR USD + attempt( + Sell, + "N:N", + q1, + {"2", "2"}, + "10", + q1, + {"2", "2"}, + "10", + {"2", "2"}, + eur(), + usd(), + rate, + rate); + if (NumberSwitchOver) + { + attempt( + Sell, + "N:B", + q1, + {"4", "4"}, + "10", + q1, + {"4", "4"}, + "4", + {"2.666666666666667", "2.666666666666667"}, + eur(), + usd(), + rate, + rate); + } + else + { + attempt( + Sell, + "N:B", + q1, + {"4", "4"}, + "10", + q1, + {"4", "4"}, + "4", + {"2.666666666666666", "2.666666666666666"}, + eur(), + usd(), + rate, + rate); + } + attempt( + Buy, + "N:T", + q1, + {"1", "1"}, + "10", + q1, + {"2", "2"}, + "10", + {"1", "1"}, + eur(), + usd(), + rate, + rate); + attempt( + Buy, + "N:BT", + q1, + {"2", "2"}, + "10", + q1, + {"6", "6"}, + "5", + {"2", "2"}, + eur(), + usd(), + rate, + rate); + attempt( + Buy, + "N:TB", + q1, + {"2", "2"}, + "2", + q1, + {"6", "6"}, + "1", + {"0.6666666666666667", "0.6666666666666667"}, + eur(), + usd(), + rate, + rate); + if (NumberSwitchOver) + { + attempt( + Sell, + "A:N", + q1, + {"2", "2"}, + "2.5", + q1, + {"2", "2"}, + "10", + {"1.666666666666667", "1.666666666666667"}, + eur(), + usd(), + rate, + rate); + } + else + { + attempt( + Sell, + "A:N", + q1, + {"2", "2"}, + "2.5", + q1, + {"2", "2"}, + "10", + {"1.666666666666666", "1.666666666666666"}, + eur(), + usd(), + rate, + rate); + } + } } void diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 8841d2d2f..2a85a57e1 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -543,6 +543,9 @@ public: void enableFeature(uint256 const feature); + void + disableFeature(uint256 const feature); + private: void fund(bool setDefaultRipple, STAmount const& amount, Account const& account); diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 900b9812d..41eac3204 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -466,6 +466,14 @@ Env::enableFeature(uint256 const feature) app().config().features.insert(feature); } +void +Env::disableFeature(uint256 const feature) +{ + // Env::close() must be called for feature + // enable to take place. + app().config().features.erase(feature); +} + } // namespace jtx } // namespace test