mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-18 12:40:38 +00:00
Compare commits
4 Commits
pratik/ote
...
ximinez/nu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e305a7546e | ||
|
|
cbf2c87d7c | ||
|
|
e0ced0e375 | ||
|
|
e17d381879 |
@@ -260,6 +260,11 @@ public:
|
||||
unsigned
|
||||
pop() noexcept;
|
||||
|
||||
// if true, there are no recoverable digits in the guard, though there may be dropped digits
|
||||
// (xbit_)
|
||||
[[nodiscard]] bool
|
||||
unrecoverable() const noexcept;
|
||||
|
||||
// if true, there are no digits in the guard, including dropped digits (xbit_)
|
||||
[[nodiscard]] bool
|
||||
empty() const noexcept;
|
||||
@@ -277,6 +282,17 @@ public:
|
||||
void
|
||||
doDropDigit(T& mantissa, int& exponent) noexcept;
|
||||
|
||||
/**
|
||||
* Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in
|
||||
* this Guard.
|
||||
*
|
||||
* If a drop will not do anything meaningful (there are no recoverable digits in the guard, and
|
||||
* the mantissa is 0), and if targetExponent > exponent, simply set exponent to targetExponent.
|
||||
*/
|
||||
template <class T>
|
||||
void
|
||||
doDropDigitWithTarget(T& mantissa, int& exponent, int const targetExponent) noexcept;
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
@@ -374,10 +390,16 @@ Number::Guard::pop() noexcept
|
||||
return d;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::Guard::unrecoverable() const noexcept
|
||||
{
|
||||
return digits_ == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::Guard::empty() const noexcept
|
||||
{
|
||||
return digits_ == 0 && !xbit_;
|
||||
return unrecoverable() && !xbit_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -401,6 +423,22 @@ Number::Guard::doDropDigit<uint128_t>(uint128_t& mantissa, int& exponent) noexce
|
||||
++exponent;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
Number::Guard::doDropDigitWithTarget(T& mantissa, int& exponent, int const targetExponent) noexcept
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
targetExponent > exponent, "Number::Guard::doDropDigitWithTarget : something to do");
|
||||
if (mantissa == 0 && unrecoverable() && targetExponent > exponent)
|
||||
{
|
||||
// No number of dropped digits is going to change any of the operative parameters at this
|
||||
// point.
|
||||
exponent = targetExponent;
|
||||
return;
|
||||
}
|
||||
doDropDigit(mantissa, exponent);
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::pushOverflow(T mantissa)
|
||||
@@ -935,6 +973,8 @@ Number::operator+=(Number const& y)
|
||||
// 1. First, shrink the mantissa of shrinkM/shrinkE while shrinkM ends in 0.
|
||||
while (shrinkE < expandE && shrinkM % 10 == 0)
|
||||
{
|
||||
// Don't use doDropDigitWithTarget here, because the loop will stop before the
|
||||
// mantissa gets to 0.
|
||||
g.doDropDigit(shrinkM, shrinkE);
|
||||
}
|
||||
|
||||
@@ -952,7 +992,7 @@ Number::operator+=(Number const& y)
|
||||
// digits will be put into the Guard. This is the only step for non-Enabled330 modes.
|
||||
while (shrinkE < expandE)
|
||||
{
|
||||
g.doDropDigit(shrinkM, shrinkE);
|
||||
g.doDropDigitWithTarget(shrinkM, shrinkE, expandE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <exception>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@@ -176,61 +177,32 @@ struct STNumber_test : public beast::unit_test::Suite
|
||||
numberFromJson(sfNumber, std::to_string(kUMax)) ==
|
||||
STNumber(sfNumber, Number(kUMax, 0)));
|
||||
|
||||
auto const expectJsonThrows = [this](
|
||||
json::Value const& num, std::string const& expected) {
|
||||
try
|
||||
{
|
||||
numberFromJson(sfNumber, num);
|
||||
fail();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Json: " << num.asString() << " got exception: " << e.what()
|
||||
<< ", expected: " << expected;
|
||||
BEAST_EXPECTS(std::string(e.what()) == expected, out.str());
|
||||
}
|
||||
};
|
||||
|
||||
// Obvious overflows tested here
|
||||
expectJsonThrows("1e2000000", "Number::normalize 2");
|
||||
expectJsonThrows("1e2000000000", "Number::normalize 2");
|
||||
|
||||
// Obvious non-numbers tested here
|
||||
try
|
||||
{
|
||||
auto _ = numberFromJson(sfNumber, "");
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
std::string const expected = "'' is not a number";
|
||||
BEAST_EXPECT(e.what() == expected);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto _ = numberFromJson(sfNumber, "e");
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
std::string const expected = "'e' is not a number";
|
||||
BEAST_EXPECT(e.what() == expected);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto _ = numberFromJson(sfNumber, "1e");
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
std::string const expected = "'1e' is not a number";
|
||||
BEAST_EXPECT(e.what() == expected);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto _ = numberFromJson(sfNumber, "e2");
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
std::string const expected = "'e2' is not a number";
|
||||
BEAST_EXPECT(e.what() == expected);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto _ = numberFromJson(sfNumber, json::Value());
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
std::string const expected = "not a number";
|
||||
BEAST_EXPECT(e.what() == expected);
|
||||
}
|
||||
expectJsonThrows("", "'' is not a number");
|
||||
expectJsonThrows("e", "'e' is not a number");
|
||||
expectJsonThrows("1e", "'1e' is not a number");
|
||||
expectJsonThrows("e2", "'e2' is not a number");
|
||||
expectJsonThrows(json::Value(), "not a number");
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <xrpl/basics/Number.h>
|
||||
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
@@ -183,6 +184,17 @@ TEST(NumberTest, limits)
|
||||
}
|
||||
EXPECT_TRUE(caught);
|
||||
|
||||
try
|
||||
{
|
||||
Number{1, 2000000, Number::Normalized{}};
|
||||
ADD_FAILURE();
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
std::string const expected = "Number::normalize 2";
|
||||
EXPECT_EQ(e.what(), expected) << e.what();
|
||||
}
|
||||
|
||||
if (scale == MantissaRange::MantissaScale::Large330)
|
||||
{
|
||||
// Normalization with the other scales, including the older large mantissa scales, will
|
||||
@@ -403,6 +415,37 @@ TEST(NumberTest, add)
|
||||
}
|
||||
EXPECT_TRUE(caught);
|
||||
}
|
||||
|
||||
// Special case: Exponents at each end of the allowable range
|
||||
for (auto const round :
|
||||
{Number::RoundingMode::ToNearest,
|
||||
Number::RoundingMode::TowardsZero,
|
||||
Number::RoundingMode::Downward,
|
||||
Number::RoundingMode::Upward})
|
||||
{
|
||||
NumberRoundModeGuard const rg{round};
|
||||
auto const x =
|
||||
Number{Number::minMantissa(), Number::kMaxExponent, Number::Normalized{}};
|
||||
auto const y =
|
||||
Number{Number::minMantissa(), Number::kMinExponent, Number::Normalized{}};
|
||||
EXPECT_EQ(x.exponent(), Number::kMaxExponent);
|
||||
EXPECT_NE(x, beast::kZero);
|
||||
EXPECT_EQ(y.exponent(), Number::kMinExponent);
|
||||
EXPECT_NE(y, beast::kZero);
|
||||
auto const result = x + y;
|
||||
|
||||
if (round == Number::RoundingMode::Upward)
|
||||
{
|
||||
// Rounding upward will take that little x-bit and round result up to the next
|
||||
// representable value.
|
||||
EXPECT_NE(result, x);
|
||||
EXPECT_EQ(result, (Number{x.mantissa() + 1, x.exponent()}));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_EQ(result, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user