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:
Howard Hinnant
2022-08-12 10:33:43 -04:00
committed by Elliot Lee
parent a82ad5ba76
commit 3f33471220
3 changed files with 226 additions and 39 deletions

View File

@@ -23,6 +23,7 @@
#include <numeric>
#include <stdexcept>
#include <type_traits>
#include <utility>
#ifdef _MSVC_LANG
#include <boost/multiprecision/cpp_int.hpp>
@@ -33,6 +34,20 @@ using uint128_t = __uint128_t;
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
// The Guard class is used to tempoarily add extra digits of
@@ -107,16 +122,40 @@ Number::Guard::pop() noexcept
return d;
}
// Returns:
// -1 if Guard is less than half
// 0 if Guard is exactly half
// 1 if Guard is greater than half
int
Number::Guard::round() noexcept
{
if (digits_ > 0x5000'0000'0000'0000)
return 1;
if (digits_ < 0x5000'0000'0000'0000)
return -1;
if (xbit_)
return 1;
return 0;
auto mode = Number::getround();
switch (mode)
{
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 (digits_ > 0 || xbit_)
return 1;
return -1;
}
}
// Number