Skip to content

Commit

Permalink
Merge pull request #711 from cppalliance/no_normal
Browse files Browse the repository at this point in the history
decimal32 operator< without normalization in the general case
  • Loading branch information
mborland authored Jul 16, 2024
2 parents 43c44e6 + e727f04 commit 2a17f6e
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 10 deletions.
64 changes: 55 additions & 9 deletions include/boost/decimal/decimal32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1144,23 +1144,42 @@ constexpr auto operator<(Integer lhs, decimal32 rhs) noexcept
constexpr auto operator<=(decimal32 lhs, decimal32 rhs) noexcept -> bool
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) || isnan(rhs))
if (!isfinite(lhs) || !isfinite(rhs))
{
return false;
if (isnan(lhs) || isnan(rhs))
{
return false;
}
if (isinf(lhs))
{
return signbit(lhs);
}
else if (isinf(rhs))
{
return !signbit(rhs);
}
}
#endif

return !(rhs < lhs);
return !less_parts_impl(rhs.full_significand(), rhs.biased_exponent(), rhs.isneg(),
lhs.full_significand(), lhs.biased_exponent(), lhs.isneg());
}

template <typename Integer>
constexpr auto operator<=(decimal32 lhs, Integer rhs) noexcept
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool)
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs))
if (!isfinite(lhs))
{
return false;
if (isnan(lhs))
{
return false;
}
else if (isinf(lhs))
{
return signbit(lhs);
}
}
#endif

Expand All @@ -1172,9 +1191,16 @@ constexpr auto operator<=(Integer lhs, decimal32 rhs) noexcept
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool)
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(rhs))
if (!isfinite(rhs))
{
return false;
if (isnan(rhs))
{
return false;
}
else if (isinf(rhs))
{
return !signbit(rhs);
}
}
#endif

Expand All @@ -1183,7 +1209,26 @@ constexpr auto operator<=(Integer lhs, decimal32 rhs) noexcept

constexpr auto operator>(decimal32 lhs, decimal32 rhs) noexcept -> bool
{
return rhs < lhs;
#ifndef BOOST_DECIMAL_FAST_MATH
if (!isfinite(lhs) || !isfinite(rhs))
{
if (isnan(lhs) || isnan(rhs))
{
return false;
}
if (isinf(lhs))
{
return !signbit(lhs);
}
else if (isinf(rhs))
{
return signbit(rhs);
}
}
#endif

return less_parts_impl(rhs.full_significand(), rhs.biased_exponent(), rhs.isneg(),
lhs.full_significand(), lhs.biased_exponent(), lhs.isneg());
}

template <typename Integer>
Expand Down Expand Up @@ -1223,7 +1268,8 @@ constexpr auto operator>=(decimal32 lhs, decimal32 rhs) noexcept -> bool
}
#endif

return !(lhs < rhs);
return !less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(),
rhs.full_significand(), rhs.biased_exponent(), rhs.isneg());
}

template <typename Integer>
Expand Down
63 changes: 62 additions & 1 deletion include/boost/decimal/detail/comparison.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,68 @@ constexpr auto operator!=(Decimal1 lhs, Decimal2 rhs) noexcept
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType = decimal32, BOOST_DECIMAL_INTEGRAL T1,
BOOST_DECIMAL_INTEGRAL U1, BOOST_DECIMAL_INTEGRAL T2, BOOST_DECIMAL_INTEGRAL U2>
constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<std::is_same<DecimalType, decimal32>::value || std::is_same<DecimalType, decimal64>::value || std::is_same<DecimalType, decimal128>::value, bool>
T2 rhs_sig, U2 rhs_exp, bool rhs_sign, bool normalized = false) noexcept -> std::enable_if_t<std::is_same<DecimalType, decimal32>::value, bool>
{
using comp_type = std::uint_fast64_t;

BOOST_DECIMAL_ASSERT(lhs_sig >= 0);
BOOST_DECIMAL_ASSERT(rhs_sig >= 0);

if (lhs_sign != rhs_sign)
{
return lhs_sign;
}

auto new_lhs_sig {static_cast<comp_type>(lhs_sig)};
auto new_rhs_sig {static_cast<comp_type>(rhs_sig)};

if (new_lhs_sig == UINT64_C(0) || new_rhs_sig == UINT64_C(0))
{
return (new_lhs_sig == new_rhs_sig) ? false : (new_lhs_sig == 0 ? !rhs_sign : lhs_sign);
}

const auto delta_exp {lhs_exp - rhs_exp};
constexpr auto max_delta_diff {std::numeric_limits<std::uint_fast64_t>::digits10 - detail::precision_v<DecimalType>};

// If we can't do this correctly without normalization then do it and try again
if (delta_exp > max_delta_diff || delta_exp < -max_delta_diff)
{
if (!normalized)
{
detail::normalize(lhs_sig, lhs_exp);
detail::normalize(rhs_sig, rhs_exp);
return less_parts_impl(lhs_sig, lhs_exp, lhs_sign,
rhs_sig, rhs_exp, rhs_sign, true);
}
else
{
return rhs_sign ? rhs_exp < lhs_exp : rhs_exp > lhs_exp;
}
}

if (delta_exp >= 0)
{
new_lhs_sig *= detail::pow10(static_cast<comp_type>(delta_exp));
lhs_exp -= delta_exp;
}
else
{
new_rhs_sig *= detail::pow10(static_cast<comp_type>(-delta_exp));
rhs_exp += delta_exp;
}

if (lhs_exp != rhs_exp)
{
return lhs_sign ? lhs_exp > rhs_exp : lhs_exp < rhs_exp;
}

return lhs_sign ? new_lhs_sig > new_rhs_sig : new_lhs_sig < new_rhs_sig;
}

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType = decimal32, BOOST_DECIMAL_INTEGRAL T1,
BOOST_DECIMAL_INTEGRAL U1, BOOST_DECIMAL_INTEGRAL T2, BOOST_DECIMAL_INTEGRAL U2>
constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<std::is_same<DecimalType, decimal64>::value || std::is_same<DecimalType, decimal128>::value, bool>
{
using comp_type = std::conditional_t<std::is_same<DecimalType, decimal128>::value, detail::uint128, std::uint_fast64_t>;

Expand Down
19 changes: 19 additions & 0 deletions test/random_decimal32_comp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,23 @@ void random_mixed_GE(T lower, T upper)
BOOST_TEST(!(dist(rng) >= std::numeric_limits<decimal32>::quiet_NaN()));
}

template <typename T>
void spot_test_mixed_ge(T lhs, T rhs)
{
const decimal32 val1 {lhs};
const T val2 {static_cast<T>(decimal32(rhs))};

if (!BOOST_TEST_EQ(val1 >= val2, lhs >= rhs))
{
// LCOV_EXCL_START
std::cerr << " LHS: " << lhs
<< "\nLHS D: " << val1
<< "\n RHS: " << rhs
<< "\nRHS D: " << val2 << std::endl;
// LCOV_EXCL_STOP
}
}

template <typename T>
void random_EQ(T lower, T upper)
{
Expand Down Expand Up @@ -543,6 +560,8 @@ int main()
random_mixed_GE(std::numeric_limits<long long>::min(), std::numeric_limits<long long>::max());
random_mixed_GE(std::numeric_limits<unsigned long long>::min(), std::numeric_limits<unsigned long long>::max());

spot_test_mixed_ge(UINT64_C(15984034765439402622), UINT64_C(1366685175759710132));

random_EQ(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
random_EQ(std::numeric_limits<unsigned>::min(), std::numeric_limits<unsigned>::max());
random_EQ(std::numeric_limits<long>::min(), std::numeric_limits<long>::max());
Expand Down
28 changes: 28 additions & 0 deletions test/random_decimal32_math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,31 @@ void random_mixed_addition(T lower, T upper)
BOOST_TEST(isnan(dist(rng) + std::numeric_limits<decimal32>::quiet_NaN()));
}

template <typename T>
void spot_random_mixed_addition(T lhs, T rhs)
{
const T val1 {lhs};
const T val2 {rhs};

const decimal32 dec1 {val1};
const T trunc_val_2 {static_cast<T>(decimal32(val2))};

const decimal32 res = dec1 + trunc_val_2;
const auto res_int = static_cast<T>(res);

if (!BOOST_TEST_EQ(res_int, val1 + val2))
{
// LCOV_EXCL_START
std::cerr << "Val 1: " << val1
<< "\nDec 1: " << dec1
<< "\nVal 2: " << val2
<< "\nDec 2: " << trunc_val_2
<< "\nDec res: " << res
<< "\nInt res: " << val1 + val2 << std::endl;
// LCOV_EXCL_STOP
}
}

template <typename T>
void random_converted_addition(T lower, T upper)
{
Expand Down Expand Up @@ -993,6 +1018,9 @@ int main()
random_right_shift();
random_mixed_right_shift();

spot_random_mixed_addition(-653573LL, 1391401LL);
spot_random_mixed_addition(894090LL, -1886315LL);

return boost::report_errors();
}

Expand Down

0 comments on commit 2a17f6e

Please sign in to comment.