Skip to content

Commit

Permalink
Merge pull request #729 from cppalliance/better_comps
Browse files Browse the repository at this point in the history
  • Loading branch information
mborland committed Sep 23, 2024
2 parents 0478b0c + 7f63f4f commit 9543df5
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 17 deletions.
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

0 comments on commit 9543df5

Please sign in to comment.