Remove undefined behavior

* Taking the negative of a signed negative is UB, but
  taking the negative of an unsigned is not.
This commit is contained in:
Howard Hinnant
2022-09-15 11:31:15 -04:00
committed by Elliot Lee
parent d275a2ab72
commit 6fcd654bee
6 changed files with 255 additions and 183 deletions

View File

@@ -45,6 +45,12 @@ IOUAmount::minPositiveAmount()
void
IOUAmount::normalize()
{
if (mantissa_ == 0)
{
*this = beast::zero;
return;
}
if (*stNumberSwitchover)
{
Number v{mantissa_, exponent_};
@@ -56,11 +62,6 @@ IOUAmount::normalize()
*this = beast::zero;
return;
}
if (mantissa_ == 0)
{
*this = beast::zero;
return;
}
bool const negative = (mantissa_ < 0);
@@ -107,48 +108,46 @@ IOUAmount::IOUAmount(Number const& other)
IOUAmount&
IOUAmount::operator+=(IOUAmount const& other)
{
if (other == beast::zero)
return *this;
if (*this == beast::zero)
{
*this = other;
return *this;
}
if (*stNumberSwitchover)
{
*this = IOUAmount{Number{*this} + Number{other}};
return *this;
}
else
auto m = other.mantissa_;
auto e = other.exponent_;
while (exponent_ < e)
{
if (other == beast::zero)
return *this;
if (*this == beast::zero)
{
*this = other;
return *this;
}
auto m = other.mantissa_;
auto e = other.exponent_;
while (exponent_ < e)
{
mantissa_ /= 10;
++exponent_;
}
while (e < exponent_)
{
m /= 10;
++e;
}
// This addition cannot overflow an std::int64_t but we may throw from
// normalize if the result isn't representable.
mantissa_ += m;
if (mantissa_ >= -10 && mantissa_ <= 10)
{
*this = beast::zero;
return *this;
}
normalize();
mantissa_ /= 10;
++exponent_;
}
while (e < exponent_)
{
m /= 10;
++e;
}
// This addition cannot overflow an std::int64_t but we may throw from
// normalize if the result isn't representable.
mantissa_ += m;
if (mantissa_ >= -10 && mantissa_ <= 10)
{
*this = beast::zero;
return *this;
}
normalize();
return *this;
}

View File

@@ -25,7 +25,7 @@
#include <type_traits>
#include <utility>
#ifdef _MSVC_LANG
#ifdef BOOST_COMP_MSVC
#include <boost/multiprecision/cpp_int.hpp>
using uint128_t = boost::multiprecision::uint128_t;
#else // !defined(_MSVC_LANG)
@@ -130,34 +130,37 @@ int
Number::Guard::round() noexcept
{
auto mode = Number::getround();
switch (mode)
if (mode == towards_zero)
return -1;
if (mode == downward)
{
// round to nearest if mode is not one of the predefined values
default:
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 (sbit_)
{
if (digits_ > 0 || xbit_)
return 1;
return -1;
}
return -1;
}
if (mode == upward)
{
if (sbit_)
return -1;
if (digits_ > 0 || xbit_)
return 1;
return -1;
}
// assume round to nearest if mode is not one of the predefined values
if (digits_ > 0x5000'0000'0000'0000)
return 1;
if (digits_ < 0x5000'0000'0000'0000)
return -1;
if (xbit_)
return 1;
return 0;
}
// Number
@@ -173,9 +176,9 @@ Number::normalize()
return;
}
bool const negative = (mantissa_ < 0);
if (negative)
mantissa_ = -mantissa_;
auto m = static_cast<std::make_unsigned_t<rep>>(mantissa_);
if (negative)
m = -m;
while ((m < minMantissa) && (exponent_ > minExponent))
{
m *= 10;