mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-03 18:28:07 +00:00
Merge branch 'ximinez/number-round-maxrep-down' into ximinez/number-round-maxrep
* ximinez/number-round-maxrep-down: Revert "Rollback Number class changes; show the fix works without side effects" Rollback Number class changes; show the fix works without side effects Include rounding in failed unit tests Improve comment descriptions Rework subtraction rounding (again) for more accuracy build: Create single test binary xrpl_tests (7327) ci: [DEPENDABOT] bump actions/checkout from 6.0.2 to 6.0.3 (7414) ci: Refactor build-related nix / docker / workflows (7408) refactor: Construct Number::Guard from MantissaRange or relevant fields ci: Use multiple directories in dependabot config (7413) ci: Update clang-tidy to nix-based v22 (7412) clang-tidy: template param names, const correctness, braces
This commit is contained in:
@@ -43,12 +43,17 @@ class Number_test : public beast::unit_test::Suite
|
||||
return out;
|
||||
}
|
||||
|
||||
static BigInt
|
||||
BigInt
|
||||
toBigInt(Number const& n)
|
||||
{
|
||||
BigInt v = n.mantissa();
|
||||
for (int i = 0; i < n.exponent(); ++i)
|
||||
v *= 10;
|
||||
for (int i = 0; i > n.exponent(); --i)
|
||||
{
|
||||
BEAST_EXPECT(v % 10 == 0);
|
||||
v /= 10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -1822,7 +1827,7 @@ public:
|
||||
BigInt const exactProduct = BigInt(kAValue) * BigInt(kBValue);
|
||||
|
||||
// What Number actually stored.
|
||||
BigInt storedValue = toBigInt(product);
|
||||
BigInt const storedValue = toBigInt(product);
|
||||
|
||||
BigInt const signedDifference = storedValue - exactProduct;
|
||||
|
||||
@@ -2125,45 +2130,178 @@ public:
|
||||
}
|
||||
}
|
||||
{
|
||||
testcase << "operator+ TowardsZero rounds away from zero " << to_string(scale);
|
||||
testcase << "subtraction rounding " << to_string(scale);
|
||||
|
||||
Number const a{1LL, 20};
|
||||
Number const b{-1'000'000'000'000'000'001LL};
|
||||
|
||||
BEAST_EXPECT(toBigInt(a) == BigInt{"100000000000000000000"});
|
||||
if (scale != MantissaRange::MantissaScale::Small)
|
||||
BEAST_EXPECT(toBigInt(b) == BigInt{"-1000000000000000001"});
|
||||
else
|
||||
BEAST_EXPECT(toBigInt(b) == BigInt{"-1000000000000000000"});
|
||||
|
||||
Number sum;
|
||||
{
|
||||
NumberRoundModeGuard const roundGuard{Number::RoundingMode::TowardsZero};
|
||||
sum = a + b;
|
||||
}
|
||||
|
||||
BigInt const exact = toBigInt(a) + toBigInt(b);
|
||||
BigInt const stored = toBigInt(sum);
|
||||
BigInt const diff = stored - exact;
|
||||
|
||||
log << " a = " << a << "\n b = " << b
|
||||
<< "\n exact a + b = " << exact.str() << "\n TowardsZero = " << stored.str()
|
||||
<< "\n difference = " << diff.str() << "\n\n";
|
||||
log.flush();
|
||||
auto const exp = Number::mantissaLog();
|
||||
Number const a{1LL, exp + 2};
|
||||
Number const b{-(Number{1, exp} + 1)};
|
||||
|
||||
if (scale == MantissaRange::MantissaScale::Small)
|
||||
{
|
||||
BEAST_EXPECT(stored == exact);
|
||||
}
|
||||
else if (scale == MantissaRange::MantissaScale::LargeLegacy)
|
||||
{
|
||||
BEAST_EXPECT(stored > exact);
|
||||
BEAST_EXPECT(toBigInt(a) == BigInt{"100000000000000000"});
|
||||
BEAST_EXPECT(toBigInt(b) == BigInt{"-1000000000000001"});
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(stored < exact);
|
||||
BEAST_EXPECT(diff < 0);
|
||||
BEAST_EXPECT(-diff < pow10<BigInt>(sum.exponent()));
|
||||
BEAST_EXPECT(toBigInt(a) == BigInt{"100000000000000000000"});
|
||||
BEAST_EXPECT(toBigInt(b) == BigInt{"-1000000000000000001"});
|
||||
}
|
||||
|
||||
auto construct = [&a, &b, this](Number::RoundingMode r) {
|
||||
NumberRoundModeGuard const roundGuard{r};
|
||||
auto const sum = a + b;
|
||||
BigInt const stored = toBigInt(sum);
|
||||
return std::make_pair(r, std::make_pair(stored, sum));
|
||||
};
|
||||
|
||||
auto const bigA = toBigInt(a);
|
||||
auto const bigB = toBigInt(b);
|
||||
BigInt const exact = bigA + bigB;
|
||||
|
||||
auto const sums = [&]() {
|
||||
std::map<Number::RoundingMode, std::pair<BigInt, Number>> sums;
|
||||
sums.emplace(construct(Number::RoundingMode::TowardsZero));
|
||||
sums.emplace(construct(Number::RoundingMode::Upward));
|
||||
sums.emplace(construct(Number::RoundingMode::Downward));
|
||||
sums.emplace(construct(Number::RoundingMode::ToNearest));
|
||||
return sums;
|
||||
}();
|
||||
|
||||
log << " a = " << a << " (" << fmt(bigA) << ")\n b = " << b
|
||||
<< " (" << fmt(bigB) << ")\n exact a + b = " << fmt(exact) << "\n";
|
||||
for (auto const& [r, sum] : sums)
|
||||
{
|
||||
auto const diff = sum.first - exact;
|
||||
auto const rLabel = to_string(r);
|
||||
log << std::string(15 - rLabel.length(), ' ') << rLabel << " = " << fmt(sum.first)
|
||||
<< "\n difference = " << fmt(diff) << "\n";
|
||||
}
|
||||
log << "\n";
|
||||
log.flush();
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case MantissaRange::MantissaScale::Small:
|
||||
case MantissaRange::MantissaScale::LargeLegacy: {
|
||||
// Without the fix, all the results but one round up
|
||||
for (auto const& [r, sum] : sums)
|
||||
{
|
||||
if (r == Number::RoundingMode::Downward)
|
||||
{
|
||||
// Downward works because the Guard sign is negative, and Downward
|
||||
// returns Up instead of Down if negative and there's a remainder,
|
||||
// whereas TowardsZero always returns Down.
|
||||
BEAST_EXPECTS(
|
||||
sums.at(Number::RoundingMode::Downward).first < exact,
|
||||
to_string(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECTS(sums.at(r).first > exact, to_string(r));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
for (auto const& [r, sum] : sums)
|
||||
{
|
||||
auto const epsilon = pow10<BigInt>(sum.second.exponent());
|
||||
BEAST_EXPECT(epsilon == 100);
|
||||
auto diff = sum.first - exact;
|
||||
switch (r)
|
||||
{
|
||||
case Number::RoundingMode::Upward:
|
||||
case Number::RoundingMode::ToNearest:
|
||||
BEAST_EXPECTS(sum.first > exact, to_string(r));
|
||||
BEAST_EXPECTS(diff < epsilon, to_string(r));
|
||||
break;
|
||||
default:
|
||||
BEAST_EXPECTS(sum.first < exact, to_string(r));
|
||||
BEAST_EXPECTS(-diff < epsilon, to_string(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
auto const offset = 30;
|
||||
testcase << "subtraction rounding offset of " << offset << " " << to_string(scale);
|
||||
|
||||
auto const exp = Number::mantissaLog();
|
||||
Number const a{1LL, exp + offset};
|
||||
Number const b{-1};
|
||||
|
||||
auto construct = [&a, &b, this](Number::RoundingMode r) {
|
||||
NumberRoundModeGuard const roundGuard{r};
|
||||
auto const sum = a + b;
|
||||
BigInt const stored = toBigInt(sum);
|
||||
return std::make_pair(r, std::make_pair(stored, sum));
|
||||
};
|
||||
|
||||
auto const bigA = toBigInt(a);
|
||||
auto const bigB = toBigInt(b);
|
||||
BigInt const exact = bigA + bigB;
|
||||
|
||||
auto const sums = [&]() {
|
||||
std::map<Number::RoundingMode, std::pair<BigInt, Number>> sums;
|
||||
sums.emplace(construct(Number::RoundingMode::TowardsZero));
|
||||
sums.emplace(construct(Number::RoundingMode::Upward));
|
||||
sums.emplace(construct(Number::RoundingMode::Downward));
|
||||
sums.emplace(construct(Number::RoundingMode::ToNearest));
|
||||
return sums;
|
||||
}();
|
||||
|
||||
log << "\n a = " << a << " (" << fmt(bigA) << ")\n b = " << b
|
||||
<< " (" << fmt(bigB) << ")\n exact a + b = " << fmt(exact) << "\n";
|
||||
for (auto const& [r, sum] : sums)
|
||||
{
|
||||
auto const diff = sum.first - exact;
|
||||
auto const rLabel = to_string(r);
|
||||
log << std::string(15 - rLabel.length(), ' ') << rLabel << " = " << fmt(sum.first)
|
||||
<< "\n difference = " << fmt(diff) << "\n";
|
||||
}
|
||||
log.flush();
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case MantissaRange::MantissaScale::Small:
|
||||
case MantissaRange::MantissaScale::LargeLegacy: {
|
||||
for (auto const& [r, sum] : sums)
|
||||
{
|
||||
if (r == Number::RoundingMode::Downward)
|
||||
{
|
||||
// Downward works because the Guard sign is negative, and Downward
|
||||
// returns Up instead of Down if negative and there's a remainder,
|
||||
// whereas TowardsZero always returns Down.
|
||||
BEAST_EXPECTS(
|
||||
sums.at(Number::RoundingMode::Downward).first < exact,
|
||||
to_string(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECTS(sums.at(r).first > exact, to_string(r));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
for (auto const& [r, sum] : sums)
|
||||
{
|
||||
auto const epsilon = pow10<BigInt>(sum.second.exponent());
|
||||
auto diff = sum.first - exact;
|
||||
switch (r)
|
||||
{
|
||||
case Number::RoundingMode::Upward:
|
||||
case Number::RoundingMode::ToNearest:
|
||||
BEAST_EXPECTS(sum.first > exact, to_string(r));
|
||||
BEAST_EXPECTS(diff < epsilon, to_string(r));
|
||||
break;
|
||||
default:
|
||||
BEAST_EXPECTS(sum.first < exact, to_string(r));
|
||||
BEAST_EXPECTS(-diff < epsilon, to_string(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user