diff --git a/include/boost/decimal/decimal32.hpp b/include/boost/decimal/decimal32.hpp index 4c8e0e6f..febaa941 100644 --- a/include/boost/decimal/decimal32.hpp +++ b/include/boost/decimal/decimal32.hpp @@ -212,6 +212,12 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_decimal_floating_point_v), bool>; + template + friend constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + + template + friend constexpr auto sequential_less_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + public: // 3.2.2.1 construct/copy/destroy: constexpr decimal32() noexcept = default; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/include/boost/decimal/decimal64.hpp b/include/boost/decimal/decimal64.hpp index ac44a243..019f0fbe 100644 --- a/include/boost/decimal/decimal64.hpp +++ b/include/boost/decimal/decimal64.hpp @@ -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 + friend constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + public: // 3.2.3.1 construct/copy/destroy constexpr decimal64() noexcept = default; diff --git a/include/boost/decimal/detail/comparison.hpp b/include/boost/decimal/detail/comparison.hpp index 091b8150..bb8b6f3d 100644 --- a/include/boost/decimal/detail/comparison.hpp +++ b/include/boost/decimal/detail/comparison.hpp @@ -24,6 +24,50 @@ namespace boost { namespace decimal { +template +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 || delta_exp < -detail::precision_v) + { + 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(delta_exp)) : + rhs_sig *= detail::pow10(static_cast(-delta_exp)); + + return lhs_sig == rhs_sig; +} + template constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign, @@ -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 +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(lhs.full_significand())}; + auto rhs_sig {static_cast(rhs.full_significand())}; + if (lhs_sig == static_cast(0) || rhs_sig == static_cast(0)) + { + return (lhs_sig == rhs_sig) ? false : (lhs_sig == static_cast(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::digits10 - detail::precision_v}; + + 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(delta_exp)); + lhs_exp -= delta_exp; + } + else + { + rhs_sig *= detail::pow10(static_cast(-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 constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,