mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-22 12:05:53 +00:00
Introduce rounding modes for Number:
You can set a thread-local flag to direct Number how to round
non-exact results with the syntax:
Number::rounding_mode prev_mode = Number::setround(Number::towards_zero);
This flag will stay in effect for this thread only until another call
to setround. The previously set rounding mode is returned.
You can also retrieve the current rounding mode with:
Number::rounding_mode current_mode = Number::getround();
The available rounding modes are:
* to_nearest : Rounds to nearest representable value. On tie, rounds
to even.
* towards_zero : Rounds towards zero.
* downward : Rounds towards negative infinity.
* upward : Rounds towards positive infinity.
The default rounding mode is to_nearest.
This commit is contained in:
committed by
Elliot Lee
parent
a82ad5ba76
commit
3f33471220
@@ -149,7 +149,17 @@ public:
|
|||||||
return os << to_string(x);
|
return os << to_string(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thread local rounding control. Default is to_nearest
|
||||||
|
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||||
|
static rounding_mode
|
||||||
|
getround();
|
||||||
|
// Returns previously set mode
|
||||||
|
static rounding_mode
|
||||||
|
setround(rounding_mode mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static thread_local rounding_mode mode_;
|
||||||
|
|
||||||
void
|
void
|
||||||
normalize();
|
normalize();
|
||||||
constexpr bool
|
constexpr bool
|
||||||
@@ -308,6 +318,9 @@ power(Number const& f, unsigned n);
|
|||||||
Number
|
Number
|
||||||
root(Number f, unsigned d);
|
root(Number f, unsigned d);
|
||||||
|
|
||||||
|
Number
|
||||||
|
root2(Number f);
|
||||||
|
|
||||||
// Returns f^(n/d)
|
// Returns f^(n/d)
|
||||||
|
|
||||||
Number
|
Number
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#ifdef _MSVC_LANG
|
#ifdef _MSVC_LANG
|
||||||
#include <boost/multiprecision/cpp_int.hpp>
|
#include <boost/multiprecision/cpp_int.hpp>
|
||||||
@@ -33,6 +34,20 @@ using uint128_t = __uint128_t;
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
thread_local Number::rounding_mode Number::mode_ = Number::to_nearest;
|
||||||
|
|
||||||
|
Number::rounding_mode
|
||||||
|
Number::getround()
|
||||||
|
{
|
||||||
|
return mode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Number::rounding_mode
|
||||||
|
Number::setround(rounding_mode mode)
|
||||||
|
{
|
||||||
|
return std::exchange(mode_, mode);
|
||||||
|
}
|
||||||
|
|
||||||
// Guard
|
// Guard
|
||||||
|
|
||||||
// The Guard class is used to tempoarily add extra digits of
|
// The Guard class is used to tempoarily add extra digits of
|
||||||
@@ -107,9 +122,17 @@ Number::Guard::pop() noexcept
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns:
|
||||||
|
// -1 if Guard is less than half
|
||||||
|
// 0 if Guard is exactly half
|
||||||
|
// 1 if Guard is greater than half
|
||||||
int
|
int
|
||||||
Number::Guard::round() noexcept
|
Number::Guard::round() noexcept
|
||||||
{
|
{
|
||||||
|
auto mode = Number::getround();
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case to_nearest:
|
||||||
if (digits_ > 0x5000'0000'0000'0000)
|
if (digits_ > 0x5000'0000'0000'0000)
|
||||||
return 1;
|
return 1;
|
||||||
if (digits_ < 0x5000'0000'0000'0000)
|
if (digits_ < 0x5000'0000'0000'0000)
|
||||||
@@ -117,6 +140,22 @@ Number::Guard::round() noexcept
|
|||||||
if (xbit_)
|
if (xbit_)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
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 (digits_ > 0 || xbit_)
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number
|
// Number
|
||||||
|
|||||||
@@ -26,6 +26,24 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
class saveNumberRoundMode
|
||||||
|
{
|
||||||
|
Number::rounding_mode mode_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~saveNumberRoundMode()
|
||||||
|
{
|
||||||
|
Number::setround(mode_);
|
||||||
|
}
|
||||||
|
explicit saveNumberRoundMode(Number::rounding_mode mode) noexcept
|
||||||
|
: mode_{mode}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
saveNumberRoundMode(saveNumberRoundMode const&) = delete;
|
||||||
|
saveNumberRoundMode&
|
||||||
|
operator=(saveNumberRoundMode const&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
class Number_test : public beast::unit_test::suite
|
class Number_test : public beast::unit_test::suite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -338,6 +356,8 @@ public:
|
|||||||
{
|
{
|
||||||
testcase("test_to_integer");
|
testcase("test_to_integer");
|
||||||
using Case = std::tuple<Number, std::int64_t>;
|
using Case = std::tuple<Number, std::int64_t>;
|
||||||
|
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
||||||
|
{
|
||||||
Case c[]{
|
Case c[]{
|
||||||
{Number{0}, 0},
|
{Number{0}, 0},
|
||||||
{Number{1}, 1},
|
{Number{1}, 1},
|
||||||
@@ -372,6 +392,121 @@ public:
|
|||||||
auto j = static_cast<std::int64_t>(x);
|
auto j = static_cast<std::int64_t>(x);
|
||||||
BEAST_EXPECT(j == y);
|
BEAST_EXPECT(j == y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
auto prev_mode = Number::setround(Number::towards_zero);
|
||||||
|
BEAST_EXPECT(prev_mode == Number::to_nearest);
|
||||||
|
{
|
||||||
|
Case c[]{
|
||||||
|
{Number{0}, 0},
|
||||||
|
{Number{1}, 1},
|
||||||
|
{Number{2}, 2},
|
||||||
|
{Number{3}, 3},
|
||||||
|
{Number{-1}, -1},
|
||||||
|
{Number{-2}, -2},
|
||||||
|
{Number{-3}, -3},
|
||||||
|
{Number{10}, 10},
|
||||||
|
{Number{99}, 99},
|
||||||
|
{Number{1155}, 1155},
|
||||||
|
{Number{9'999'999'999'999'999, 0}, 9'999'999'999'999'999},
|
||||||
|
{Number{9'999'999'999'999'999, 1}, 99'999'999'999'999'990},
|
||||||
|
{Number{9'999'999'999'999'999, 2}, 999'999'999'999'999'900},
|
||||||
|
{Number{-9'999'999'999'999'999, 2}, -999'999'999'999'999'900},
|
||||||
|
{Number{15, -1}, 1},
|
||||||
|
{Number{14, -1}, 1},
|
||||||
|
{Number{16, -1}, 1},
|
||||||
|
{Number{25, -1}, 2},
|
||||||
|
{Number{6, -1}, 0},
|
||||||
|
{Number{5, -1}, 0},
|
||||||
|
{Number{4, -1}, 0},
|
||||||
|
{Number{-15, -1}, -1},
|
||||||
|
{Number{-14, -1}, -1},
|
||||||
|
{Number{-16, -1}, -1},
|
||||||
|
{Number{-25, -1}, -2},
|
||||||
|
{Number{-6, -1}, 0},
|
||||||
|
{Number{-5, -1}, 0},
|
||||||
|
{Number{-4, -1}, 0}};
|
||||||
|
for (auto const& [x, y] : c)
|
||||||
|
{
|
||||||
|
auto j = static_cast<std::int64_t>(x);
|
||||||
|
BEAST_EXPECT(j == y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_mode = Number::setround(Number::downward);
|
||||||
|
BEAST_EXPECT(prev_mode == Number::towards_zero);
|
||||||
|
{
|
||||||
|
Case c[]{
|
||||||
|
{Number{0}, 0},
|
||||||
|
{Number{1}, 1},
|
||||||
|
{Number{2}, 2},
|
||||||
|
{Number{3}, 3},
|
||||||
|
{Number{-1}, -1},
|
||||||
|
{Number{-2}, -2},
|
||||||
|
{Number{-3}, -3},
|
||||||
|
{Number{10}, 10},
|
||||||
|
{Number{99}, 99},
|
||||||
|
{Number{1155}, 1155},
|
||||||
|
{Number{9'999'999'999'999'999, 0}, 9'999'999'999'999'999},
|
||||||
|
{Number{9'999'999'999'999'999, 1}, 99'999'999'999'999'990},
|
||||||
|
{Number{9'999'999'999'999'999, 2}, 999'999'999'999'999'900},
|
||||||
|
{Number{-9'999'999'999'999'999, 2}, -999'999'999'999'999'900},
|
||||||
|
{Number{15, -1}, 1},
|
||||||
|
{Number{14, -1}, 1},
|
||||||
|
{Number{16, -1}, 1},
|
||||||
|
{Number{25, -1}, 2},
|
||||||
|
{Number{6, -1}, 0},
|
||||||
|
{Number{5, -1}, 0},
|
||||||
|
{Number{4, -1}, 0},
|
||||||
|
{Number{-15, -1}, -2},
|
||||||
|
{Number{-14, -1}, -2},
|
||||||
|
{Number{-16, -1}, -2},
|
||||||
|
{Number{-25, -1}, -3},
|
||||||
|
{Number{-6, -1}, -1},
|
||||||
|
{Number{-5, -1}, -1},
|
||||||
|
{Number{-4, -1}, -1}};
|
||||||
|
for (auto const& [x, y] : c)
|
||||||
|
{
|
||||||
|
auto j = static_cast<std::int64_t>(x);
|
||||||
|
BEAST_EXPECT(j == y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_mode = Number::setround(Number::upward);
|
||||||
|
BEAST_EXPECT(prev_mode == Number::downward);
|
||||||
|
{
|
||||||
|
Case c[]{
|
||||||
|
{Number{0}, 0},
|
||||||
|
{Number{1}, 1},
|
||||||
|
{Number{2}, 2},
|
||||||
|
{Number{3}, 3},
|
||||||
|
{Number{-1}, -1},
|
||||||
|
{Number{-2}, -2},
|
||||||
|
{Number{-3}, -3},
|
||||||
|
{Number{10}, 10},
|
||||||
|
{Number{99}, 99},
|
||||||
|
{Number{1155}, 1155},
|
||||||
|
{Number{9'999'999'999'999'999, 0}, 9'999'999'999'999'999},
|
||||||
|
{Number{9'999'999'999'999'999, 1}, 99'999'999'999'999'990},
|
||||||
|
{Number{9'999'999'999'999'999, 2}, 999'999'999'999'999'900},
|
||||||
|
{Number{-9'999'999'999'999'999, 2}, -999'999'999'999'999'900},
|
||||||
|
{Number{15, -1}, 2},
|
||||||
|
{Number{14, -1}, 2},
|
||||||
|
{Number{16, -1}, 2},
|
||||||
|
{Number{25, -1}, 3},
|
||||||
|
{Number{6, -1}, 1},
|
||||||
|
{Number{5, -1}, 1},
|
||||||
|
{Number{4, -1}, 1},
|
||||||
|
{Number{-15, -1}, -1},
|
||||||
|
{Number{-14, -1}, -1},
|
||||||
|
{Number{-16, -1}, -1},
|
||||||
|
{Number{-25, -1}, -2},
|
||||||
|
{Number{-6, -1}, 0},
|
||||||
|
{Number{-5, -1}, 0},
|
||||||
|
{Number{-4, -1}, 0}};
|
||||||
|
for (auto const& [x, y] : c)
|
||||||
|
{
|
||||||
|
auto j = static_cast<std::int64_t>(x);
|
||||||
|
BEAST_EXPECT(j == y);
|
||||||
|
}
|
||||||
|
}
|
||||||
bool caught = false;
|
bool caught = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user