mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-07 02:36:47 +00:00
Revert "Rollback Number class changes; show the fix works without side effects"
This reverts commit 8743be8eae.
This commit is contained in:
@@ -51,37 +51,43 @@ namespace detail {
|
||||
* compile time. Doing it at runtime would be pretty wasteful and
|
||||
* inefficient.
|
||||
*/
|
||||
constexpr std::size_t kInt64Digits = 20;
|
||||
consteval std::array<std::uint64_t, kInt64Digits>
|
||||
constexpr std::size_t kUint64Digits = 20;
|
||||
constexpr std::size_t kUint128Digits = 39;
|
||||
|
||||
template <typename T, std::size_t Digits>
|
||||
consteval std::array<T, Digits>
|
||||
buildPowersOfTen()
|
||||
{
|
||||
std::array<std::uint64_t, kInt64Digits> result{};
|
||||
std::array<T, Digits> result{};
|
||||
|
||||
std::uint64_t power = 1;
|
||||
T power = 1;
|
||||
std::size_t exponent = 0;
|
||||
// end the loop early so it doesn't overflow;
|
||||
for (; exponent < result.size() - 1; ++exponent, power *= 10)
|
||||
{
|
||||
result[exponent] = power;
|
||||
if (power > std::numeric_limits<std::uint64_t>::max() / 10)
|
||||
if (power > std::numeric_limits<T>::max() / 10)
|
||||
throw std::logic_error("Power of 10 table is too big");
|
||||
}
|
||||
result[exponent] = power;
|
||||
if (power < std::numeric_limits<std::uint64_t>::max() / 10)
|
||||
throw std::logic_error("Power of 10 table is not big enough for the uint64_t type");
|
||||
if (power < std::numeric_limits<T>::max() / 10)
|
||||
throw std::logic_error("Power of 10 table is not big enough for the given type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
constexpr std::array<std::uint64_t, detail::kInt64Digits> kPowerOfTen = detail::buildPowersOfTen();
|
||||
template <typename T = std::uint64_t, std::size_t Digits = detail::kUint64Digits>
|
||||
constexpr std::array<T, Digits> kPowerOfTenImpl = detail::buildPowersOfTen<T, Digits>();
|
||||
|
||||
constexpr auto kPowerOfTen = kPowerOfTenImpl<std::uint64_t, detail::kUint64Digits>;
|
||||
|
||||
static_assert(kPowerOfTen[0] == 1);
|
||||
static_assert(kPowerOfTen[1] == 10);
|
||||
static_assert(kPowerOfTen[10] == 10'000'000'000);
|
||||
static_assert(
|
||||
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1);
|
||||
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1);
|
||||
|
||||
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
||||
*
|
||||
@@ -141,7 +147,7 @@ struct MantissaRange final
|
||||
int const log{getExponent(scale)};
|
||||
rep const min{getMin(scale, log)};
|
||||
rep const max{(min * 10) - 1};
|
||||
CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)};
|
||||
CuspRoundingFix const cuspRoundingFix{isCuspFixEnabled(scale)};
|
||||
|
||||
static MantissaRange const&
|
||||
getMantissaRange(MantissaScale scale);
|
||||
@@ -543,9 +549,15 @@ private:
|
||||
// changing the values inside the range.
|
||||
static thread_local std::reference_wrapper<MantissaRange const> kRange;
|
||||
|
||||
class Guard;
|
||||
|
||||
void
|
||||
normalize(MantissaRange const& range);
|
||||
|
||||
// Guard has the fields that we need, as well as MantissaRange, so if we have a guard, use that
|
||||
void
|
||||
normalize(Guard const& guard);
|
||||
|
||||
/** Normalize Number components to an arbitrary range.
|
||||
*
|
||||
* min/maxMantissa are parameters because this function is used by both
|
||||
@@ -560,7 +572,7 @@ private:
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled);
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix);
|
||||
|
||||
template <class T>
|
||||
friend void
|
||||
@@ -570,7 +582,7 @@ private:
|
||||
int& exponent,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix,
|
||||
bool dropped);
|
||||
|
||||
[[nodiscard]] bool
|
||||
@@ -588,8 +600,6 @@ private:
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
class Guard;
|
||||
};
|
||||
|
||||
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
|
||||
@@ -65,7 +65,7 @@ MantissaRange::getRanges()
|
||||
static_assert(kRange.log == 15);
|
||||
static_assert(kRange.min < Number::kMaxRep);
|
||||
static_assert(kRange.max < Number::kMaxRep);
|
||||
static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Disabled);
|
||||
static_assert(kRange.cuspRoundingFix == CuspRoundingFix::Disabled);
|
||||
}
|
||||
{
|
||||
[[maybe_unused]]
|
||||
@@ -76,7 +76,7 @@ MantissaRange::getRanges()
|
||||
static_assert(kRange.log == 18);
|
||||
static_assert(kRange.min < Number::kMaxRep);
|
||||
static_assert(kRange.max > Number::kMaxRep);
|
||||
static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Disabled);
|
||||
static_assert(kRange.cuspRoundingFix == CuspRoundingFix::Disabled);
|
||||
}
|
||||
{
|
||||
[[maybe_unused]]
|
||||
@@ -87,7 +87,7 @@ MantissaRange::getRanges()
|
||||
static_assert(kRange.log == 18);
|
||||
static_assert(kRange.min < Number::kMaxRep);
|
||||
static_assert(kRange.max > Number::kMaxRep);
|
||||
static_assert(kRange.cuspRoundingFixEnabled == CuspRoundingFix::Enabled);
|
||||
static_assert(kRange.cuspRoundingFix == CuspRoundingFix::Enabled);
|
||||
}
|
||||
return map;
|
||||
}();
|
||||
@@ -171,7 +171,21 @@ class Number::Guard
|
||||
std::uint8_t sbit_ : 1 {0}; // the sign of the guard digits
|
||||
|
||||
public:
|
||||
explicit Guard() = default;
|
||||
internalrep const minMantissa_;
|
||||
internalrep const maxMantissa_;
|
||||
MantissaRange::CuspRoundingFix const cuspRoundingFix_;
|
||||
|
||||
explicit Guard(
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix)
|
||||
: minMantissa_(minMantissa), maxMantissa_(maxMantissa), cuspRoundingFix_(cuspRoundingFix)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Guard(MantissaRange const& range) : Guard(range.min, range.max, range.cuspRoundingFix)
|
||||
{
|
||||
}
|
||||
|
||||
// set & test the sign bit
|
||||
void
|
||||
@@ -194,6 +208,10 @@ public:
|
||||
unsigned
|
||||
pop() noexcept;
|
||||
|
||||
// if true, there are no digits in the guard, including dropped digits (xbit_)
|
||||
bool
|
||||
empty() const noexcept;
|
||||
|
||||
/** Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in
|
||||
* this Guard.
|
||||
*
|
||||
@@ -206,28 +224,35 @@ public:
|
||||
void
|
||||
doDropDigit(T& mantissa, int& exponent) noexcept;
|
||||
|
||||
enum class Round {
|
||||
// The result is exact. No rounding is needed. Only used if cuspRoundingFix is enabled.
|
||||
Exact = -2,
|
||||
// Round down. Since we use integer math, that usually means no change is needed.
|
||||
// Exceptions are for when the result is between kMaxRap and kMaxRepUp (round to kMaxRep),
|
||||
// or after subtraction where _any_ remainder will modify the result. The latter is what
|
||||
// distinguishes Exact from Down.
|
||||
Down = -1,
|
||||
// The result was exactly half-way between two integers. This will round to even.
|
||||
Even = 0,
|
||||
// Round up. Always adds 1 (or subtracts 1 in some cases if cuspRoundingFix is not enabled)
|
||||
Up = 1,
|
||||
};
|
||||
|
||||
// Indicate round direction: 1 is up, -1 is down, 0 is even
|
||||
// This enables the client to round towards nearest, and on
|
||||
// tie, round towards even.
|
||||
[[nodiscard]] int
|
||||
[[nodiscard]] Round
|
||||
round() const noexcept;
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
doRoundUp(
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
|
||||
std::string location);
|
||||
doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
||||
doRoundDown(bool& negative, T& mantissa, int& exponent);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
@@ -239,7 +264,7 @@ private:
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
||||
bringIntoRange(bool& negative, T& mantissa, int& exponent);
|
||||
};
|
||||
|
||||
inline void
|
||||
@@ -289,6 +314,12 @@ Number::Guard::pop() noexcept
|
||||
return d;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::Guard::empty() const noexcept
|
||||
{
|
||||
return digits_ == 0 && !xbit_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
Number::Guard::doDropDigit(T& mantissa, int& exponent) noexcept
|
||||
@@ -314,54 +345,56 @@ Number::Guard::doDropDigit<uint128_t>(uint128_t& mantissa, int& exponent) noexce
|
||||
// -1 if Guard is less than half
|
||||
// 0 if Guard is exactly half
|
||||
// 1 if Guard is greater than half
|
||||
int
|
||||
Number::Guard::Round
|
||||
Number::Guard::round() const noexcept
|
||||
{
|
||||
auto mode = Number::getround();
|
||||
|
||||
if (cuspRoundingFix_ != MantissaRange::CuspRoundingFix::Disabled && empty())
|
||||
{
|
||||
// No remainder
|
||||
return Round::Exact;
|
||||
}
|
||||
|
||||
if (mode == RoundingMode::TowardsZero)
|
||||
return -1;
|
||||
return Round::Down;
|
||||
|
||||
if (mode == RoundingMode::Downward)
|
||||
{
|
||||
if (sbit_)
|
||||
{
|
||||
if (digits_ > 0 || xbit_)
|
||||
return 1;
|
||||
return Round::Up;
|
||||
}
|
||||
return -1;
|
||||
return Round::Down;
|
||||
}
|
||||
|
||||
if (mode == RoundingMode::Upward)
|
||||
{
|
||||
if (sbit_)
|
||||
return -1;
|
||||
return Round::Down;
|
||||
if (digits_ > 0 || xbit_)
|
||||
return 1;
|
||||
return -1;
|
||||
return Round::Up;
|
||||
return Round::Down;
|
||||
}
|
||||
|
||||
// assume round to nearest if mode is not one of the predefined values
|
||||
if (digits_ > 0x5000'0000'0000'0000)
|
||||
return 1;
|
||||
return Round::Up;
|
||||
if (digits_ < 0x5000'0000'0000'0000)
|
||||
return -1;
|
||||
return Round::Down;
|
||||
if (xbit_)
|
||||
return 1;
|
||||
return 0;
|
||||
return Round::Up;
|
||||
return Round::Even;
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::bringIntoRange(
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa)
|
||||
Number::Guard::bringIntoRange(bool& negative, T& mantissa, int& exponent)
|
||||
{
|
||||
// Bring mantissa back into the minMantissa / maxMantissa range AFTER
|
||||
// rounding
|
||||
if (mantissa < minMantissa)
|
||||
if (mantissa < minMantissa_)
|
||||
{
|
||||
mantissa *= 10;
|
||||
--exponent;
|
||||
@@ -378,22 +411,15 @@ Number::Guard::bringIntoRange(
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::doRoundUp(
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
|
||||
std::string location)
|
||||
Number::Guard::doRoundUp(bool& negative, T& mantissa, int& exponent, std::string location)
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (mantissa & 1) == 1))
|
||||
if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1))
|
||||
{
|
||||
auto const safeToIncrement = [&maxMantissa](auto const& mantissa) {
|
||||
return mantissa < maxMantissa && mantissa < kMaxRep;
|
||||
auto const safeToIncrement = [this](auto const& mantissa) {
|
||||
return mantissa < maxMantissa_ && mantissa < kMaxRep;
|
||||
};
|
||||
if (cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled)
|
||||
if (cuspRoundingFix_ == MantissaRange::CuspRoundingFix::Enabled)
|
||||
{
|
||||
// Ensure mantissa after incrementing fits within both the
|
||||
// min/maxMantissa range and is a valid "rep".
|
||||
@@ -414,14 +440,7 @@ Number::Guard::doRoundUp(
|
||||
safeToIncrement(mantissa),
|
||||
"xrpl::Number::Guard::doRoundUp",
|
||||
"can't recurse more than once");
|
||||
doRoundUp(
|
||||
negative,
|
||||
mantissa,
|
||||
exponent,
|
||||
minMantissa,
|
||||
maxMantissa,
|
||||
cuspRoundingFixEnabled,
|
||||
location);
|
||||
doRoundUp(negative, mantissa, exponent, location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -432,7 +451,7 @@ Number::Guard::doRoundUp(
|
||||
++mantissa;
|
||||
// Ensure mantissa after incrementing fits within both the
|
||||
// min/maxMantissa range and is a valid "rep".
|
||||
if (mantissa > maxMantissa || mantissa > kMaxRep)
|
||||
if (mantissa > maxMantissa_ || mantissa > kMaxRep)
|
||||
{
|
||||
// Don't use doDropDigit here
|
||||
mantissa /= 10;
|
||||
@@ -440,30 +459,38 @@ Number::Guard::doRoundUp(
|
||||
}
|
||||
}
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||
bringIntoRange(negative, mantissa, exponent);
|
||||
if (exponent > kMaxExponent)
|
||||
Throw<std::overflow_error>(std::string(location));
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::doRoundDown(
|
||||
bool& negative,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
internalrep const& minMantissa)
|
||||
Number::Guard::doRoundDown(bool& negative, T& mantissa, int& exponent)
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (mantissa & 1) == 1))
|
||||
if (cuspRoundingFix_ != MantissaRange::CuspRoundingFix::Disabled)
|
||||
{
|
||||
--mantissa;
|
||||
if (mantissa < minMantissa)
|
||||
// If there was any remainder, subtract 1 from the result. This is sufficient to get the
|
||||
// best rounding.
|
||||
if (r != Round::Exact)
|
||||
{
|
||||
mantissa *= 10;
|
||||
--exponent;
|
||||
--mantissa;
|
||||
}
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||
else
|
||||
{
|
||||
if (r == Round::Up || (r == Round::Even && (mantissa & 1) == 1))
|
||||
{
|
||||
--mantissa;
|
||||
if (mantissa < minMantissa_)
|
||||
{
|
||||
mantissa *= 10;
|
||||
--exponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent);
|
||||
}
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
@@ -471,7 +498,7 @@ void
|
||||
Number::Guard::doRound(rep& drops, std::string location) const
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
||||
if (r == Round::Up || (r == Round::Even && (drops & 1) == 1))
|
||||
{
|
||||
if (drops >= kMaxRep)
|
||||
{
|
||||
@@ -530,7 +557,7 @@ doNormalize(
|
||||
int& exponent,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix,
|
||||
bool dropped)
|
||||
{
|
||||
static constexpr auto kMinExponent = Number::kMinExponent;
|
||||
@@ -553,7 +580,7 @@ doNormalize(
|
||||
m *= 10;
|
||||
--exponent;
|
||||
}
|
||||
Guard g;
|
||||
Guard g(minMantissa, maxMantissa, cuspRoundingFix);
|
||||
if (negative)
|
||||
g.setNegative();
|
||||
if (dropped)
|
||||
@@ -598,14 +625,7 @@ doNormalize(
|
||||
XRPL_ASSERT_PARTS(m <= kMaxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64");
|
||||
mantissa = m;
|
||||
|
||||
g.doRoundUp(
|
||||
negative,
|
||||
mantissa,
|
||||
exponent,
|
||||
minMantissa,
|
||||
maxMantissa,
|
||||
cuspRoundingFixEnabled,
|
||||
"Number::normalize 2");
|
||||
g.doRoundUp(negative, mantissa, exponent, "Number::normalize 2");
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa >= minMantissa && mantissa <= maxMantissa,
|
||||
"xrpl::doNormalize",
|
||||
@@ -620,13 +640,12 @@ Number::normalize<uint128_t>(
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled)
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix)
|
||||
{
|
||||
// Not used by every compiler version, and thus not necessarily
|
||||
// counted by coverage build
|
||||
// LCOV_EXCL_START
|
||||
doNormalize(
|
||||
negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false);
|
||||
doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
@@ -638,13 +657,12 @@ Number::normalize<unsigned long long>(
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled)
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix)
|
||||
{
|
||||
// Not used by every compiler version, and thus not necessarily
|
||||
// counted by coverage build
|
||||
// LCOV_EXCL_START
|
||||
doNormalize(
|
||||
negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false);
|
||||
doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
@@ -656,16 +674,27 @@ Number::normalize<unsigned long>(
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled)
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix)
|
||||
{
|
||||
doNormalize(
|
||||
negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false);
|
||||
doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFix, false);
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize(MantissaRange const& range)
|
||||
{
|
||||
normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFixEnabled);
|
||||
normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFix);
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize(Guard const& guard)
|
||||
{
|
||||
normalize(
|
||||
negative_,
|
||||
mantissa_,
|
||||
exponent_,
|
||||
guard.minMantissa_,
|
||||
guard.maxMantissa_,
|
||||
guard.cuspRoundingFix_);
|
||||
}
|
||||
|
||||
// Copy the number, but set a new exponent. Because the mantissa doesn't change,
|
||||
@@ -719,7 +748,16 @@ Number::operator+=(Number const& y)
|
||||
bool const yn = y.negative_;
|
||||
uint128_t ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
Guard g;
|
||||
Guard g(kRange);
|
||||
|
||||
auto const& minMantissa = g.minMantissa_;
|
||||
auto const& maxMantissa = g.maxMantissa_;
|
||||
auto const cuspRoundingFix = g.cuspRoundingFix_;
|
||||
|
||||
// Bring the exponents of both values into agreement, so the mantissas are on the same scale
|
||||
// and can be added directly together.
|
||||
// Shrink the mantissa and bring the exponent up of the value with the lower exponent. Store any
|
||||
// dropped digits in the Guard.
|
||||
if (xe < ye)
|
||||
{
|
||||
if (xn)
|
||||
@@ -739,11 +777,6 @@ Number::operator+=(Number const& y)
|
||||
} while (xe > ye);
|
||||
}
|
||||
|
||||
auto const& range = kRange.get();
|
||||
auto const& minMantissa = range.min;
|
||||
auto const& maxMantissa = range.max;
|
||||
auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled;
|
||||
|
||||
if (xn == yn)
|
||||
{
|
||||
xm += ym;
|
||||
@@ -751,14 +784,7 @@ Number::operator+=(Number const& y)
|
||||
{
|
||||
g.doDropDigit(xm, xe);
|
||||
}
|
||||
g.doRoundUp(
|
||||
xn,
|
||||
xm,
|
||||
xe,
|
||||
minMantissa,
|
||||
maxMantissa,
|
||||
cuspRoundingFixEnabled,
|
||||
"Number::addition overflow");
|
||||
g.doRoundUp(xn, xm, xe, "Number::addition overflow");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -772,19 +798,40 @@ Number::operator+=(Number const& y)
|
||||
xe = ye;
|
||||
xn = yn;
|
||||
}
|
||||
while (xm < minMantissa && xm * 10 <= kMaxRep)
|
||||
if (cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled)
|
||||
{
|
||||
xm *= 10;
|
||||
xm -= g.pop();
|
||||
--xe;
|
||||
// Grow xm/xe and pull digits out of the Guard until it's a little bit larger than
|
||||
// maxMantissa, so that normalize will have enough information to make an accurate
|
||||
// rounding decision, but stop if the Guard empties out, because no rounding will be
|
||||
// necessary. (Normalize will pad it back into range.) Note that if any digits were lost
|
||||
// (xbit), the Guard will never be empty, so xm will get big.
|
||||
auto const upperLimit = static_cast<uint128_t>(minMantissa) * 1000;
|
||||
while (xm < upperLimit && !g.empty())
|
||||
{
|
||||
xm *= 10;
|
||||
xm -= g.pop();
|
||||
--xe;
|
||||
}
|
||||
}
|
||||
g.doRoundDown(xn, xm, xe, minMantissa);
|
||||
else
|
||||
{
|
||||
// Grow xm/xe and pull digits out of the Guard until it's back in range.
|
||||
while (xm < minMantissa && xm * 10 <= kMaxRep)
|
||||
{
|
||||
xm *= 10;
|
||||
xm -= g.pop();
|
||||
--xe;
|
||||
}
|
||||
}
|
||||
// Round down, based on whether there is any data left in the Guard (depending on
|
||||
// cuspRoundingFix)
|
||||
g.doRoundDown(xn, xm, xe);
|
||||
}
|
||||
|
||||
doNormalize(xn, xm, xe, minMantissa, maxMantissa, cuspRoundingFix, false);
|
||||
negative_ = xn;
|
||||
mantissa_ = static_cast<internalrep>(xm);
|
||||
exponent_ = xe;
|
||||
normalize(range);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -818,14 +865,11 @@ Number::operator*=(Number const& y)
|
||||
auto ze = xe + ye;
|
||||
auto zs = xs * ys;
|
||||
bool zn = (zs == -1);
|
||||
Guard g;
|
||||
Guard g(kRange);
|
||||
if (zn)
|
||||
g.setNegative();
|
||||
|
||||
auto const& range = kRange.get();
|
||||
auto const& minMantissa = range.min;
|
||||
auto const& maxMantissa = range.max;
|
||||
auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled;
|
||||
auto const& maxMantissa = g.maxMantissa_;
|
||||
|
||||
while (zm > maxMantissa || zm > kMaxRep)
|
||||
{
|
||||
@@ -834,19 +878,12 @@ Number::operator*=(Number const& y)
|
||||
|
||||
xm = static_cast<internalrep>(zm);
|
||||
xe = ze;
|
||||
g.doRoundUp(
|
||||
zn,
|
||||
xm,
|
||||
xe,
|
||||
minMantissa,
|
||||
maxMantissa,
|
||||
cuspRoundingFixEnabled,
|
||||
"Number::multiplication overflow : exponent is " + std::to_string(xe));
|
||||
g.doRoundUp(zn, xm, xe, "Number::multiplication overflow : exponent is " + std::to_string(xe));
|
||||
negative_ = zn;
|
||||
mantissa_ = xm;
|
||||
exponent_ = xe;
|
||||
|
||||
normalize(range);
|
||||
normalize(g);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -882,7 +919,7 @@ Number::operator/=(Number const& y)
|
||||
auto const& range = kRange.get();
|
||||
auto const& minMantissa = range.min;
|
||||
auto const& maxMantissa = range.max;
|
||||
auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled;
|
||||
auto const cuspRoundingFix = range.cuspRoundingFix;
|
||||
|
||||
// Division operates on two large integers (16-digit for small
|
||||
// mantissas, 19-digit for large) using integer math. If the values
|
||||
@@ -1014,14 +1051,14 @@ Number::operator/=(Number const& y)
|
||||
// rounding fix is enabled, flag if there is still
|
||||
// a remainder from stage 2.
|
||||
bool const useTrailingRemainder =
|
||||
cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled;
|
||||
cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled;
|
||||
if (useTrailingRemainder)
|
||||
{
|
||||
dropped = partialNumerator % dm != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled, dropped);
|
||||
doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFix, dropped);
|
||||
negative_ = zp;
|
||||
mantissa_ = static_cast<internalrep>(zm);
|
||||
exponent_ = ze;
|
||||
@@ -1035,7 +1072,7 @@ operator rep() const
|
||||
{
|
||||
rep drops = mantissa();
|
||||
int offset = exponent();
|
||||
Guard g;
|
||||
Guard g(kRange);
|
||||
if (drops != 0)
|
||||
{
|
||||
if (negative_)
|
||||
|
||||
Reference in New Issue
Block a user