Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve comparisons for decimal32 #729

Merged
merged 3 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions include/boost/decimal/decimal32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m
-> std::enable_if_t<(detail::is_decimal_floating_point_v<Decimal1> &&
detail::is_decimal_floating_point_v<Decimal2>), bool>;

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
friend constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool;

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
friend constexpr auto sequential_less_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool;

public:
// 3.2.2.1 construct/copy/destroy:
constexpr decimal32() noexcept = default;
Expand Down Expand Up @@ -1056,15 +1062,7 @@ constexpr auto decimal32::operator-=(Integer rhs) noexcept

constexpr auto operator==(decimal32 lhs, decimal32 rhs) noexcept -> bool
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) || isnan(rhs))
{
return false;
}
#endif

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

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

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

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

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

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

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

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

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

template <typename Integer>
Expand Down
3 changes: 3 additions & 0 deletions include/boost/decimal/decimal64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ BOOST_DECIMAL_EXPORT class decimal64 final
// We can super easily combine this into a single op
friend constexpr auto not_finite(decimal64 rhs) noexcept -> bool;

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
friend constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool;

public:
// 3.2.3.1 construct/copy/destroy
constexpr decimal64() noexcept = default;
Expand Down
108 changes: 108 additions & 0 deletions include/boost/decimal/detail/comparison.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,50 @@
namespace boost {
namespace decimal {

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool
{
using comp_type = typename DecimalType::significand_type;

// Step 1: Check for NANs per IEEE 754
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) || isnan(rhs))
{
return false;
}
#endif

// Step 3: Check signs
const auto lhs_neg {lhs.isneg()};
const auto rhs_neg {rhs.isneg()};

if (lhs_neg != rhs_neg)
{
return false;
}

// Step 4: Check the exponents
// If the difference is greater than we can represent in the significand than we can assume they are different
const auto lhs_exp {lhs.biased_exponent()};
const auto rhs_exp {rhs.biased_exponent()};

const auto delta_exp {lhs_exp - rhs_exp};

if (delta_exp > detail::precision_v<DecimalType> || delta_exp < -detail::precision_v<DecimalType>)
{
return false;
}

// Step 5: Normalize the significand and compare
auto lhs_sig {lhs.full_significand()};
auto rhs_sig {rhs.full_significand()};

delta_exp >= 0 ? lhs_sig *= detail::pow10(static_cast<comp_type>(delta_exp)) :
rhs_sig *= detail::pow10(static_cast<comp_type>(-delta_exp));

return lhs_sig == 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 equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
Expand Down Expand Up @@ -197,6 +241,70 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto fast_type_less_parts_impl(T lhs_sig, U
return lhs_sign ? lhs_sig > rhs_sig : lhs_sig < rhs_sig;
}

template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
constexpr auto sequential_less_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool
{
using comp_type = std::uint_fast64_t;

// Step 1: Handle our non-finite values in their own calling functions

// Step 2: Check if they are bitwise equal:
/*
if (lhs.bits_ == rhs.bits_)
{
return false;
}
*/

// Step 3: Decode and compare signs first:
const auto lhs_sign {lhs.isneg()};
const auto rhs_sign {rhs.isneg()};

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

// Step 4: Decode the significand and do a trivial comp
auto lhs_sig {static_cast<comp_type>(lhs.full_significand())};
auto rhs_sig {static_cast<comp_type>(rhs.full_significand())};
if (lhs_sig == static_cast<comp_type>(0) || rhs_sig == static_cast<comp_type>(0))
{
return (lhs_sig == rhs_sig) ? false : (lhs_sig == static_cast<comp_type>(0) ? !rhs_sign : lhs_sign);
}

// Step 5: Decode the exponent and see if we can even compare the significands
auto lhs_exp {lhs.biased_exponent()};
auto rhs_exp {rhs.biased_exponent()};

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 (delta_exp > max_delta_diff || delta_exp < -max_delta_diff)
{
return rhs_sign ? rhs_exp < lhs_exp : rhs_exp > lhs_exp;
}

// Step 6: Approximate normalization if we need to and then get the answer
if (delta_exp >= 0)
{
lhs_sig *= detail::pow10(static_cast<comp_type>(delta_exp));
lhs_exp -= delta_exp;
}
else
{
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 ? lhs_sig > rhs_sig : lhs_sig < 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,
Expand Down
Loading