Skip to content

Commit

Permalink
Merge pull request #709 from cppalliance/comp_128
Browse files Browse the repository at this point in the history
Improve 128 bit comparisons
  • Loading branch information
mborland authored Jul 11, 2024
2 parents 80872c3 + 2b0cce3 commit 43c44e6
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 66 deletions.
49 changes: 23 additions & 26 deletions include/boost/decimal/decimal128.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,16 @@ namespace detail {

// See IEEE 754 section 3.5.2
BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_inf_mask {UINT64_C(0b0'11110'00000000'0000000000'0000000000'0000000000'0000000000'0000000000), UINT64_C(0)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint64_t d128_inf_mask_high_bits {UINT64_C(0b0'11110'00000000'0000000000'0000000000'0000000000'0000000000'0000000000)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_nan_mask {UINT64_C(0b0'11111'00000000'0000000000'0000000000'0000000000'0000000000'0000000000), UINT64_C(0)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_snan_mask {UINT64_C(0b0'11111'10000000'0000000000'0000000000'0000000000'0000000000'0000000000), UINT64_C(0)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_comb_inf_mask {UINT64_C(0b0'11110'00000000'0000000000'0000000000'0000000000'0000000000'0000000000), UINT64_C(0)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_comb_nan_mask {UINT64_C(0b0'11111'00000000'0000000000'0000000000'0000000000'0000000000'0000000000), UINT64_C(0)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_exp_snan_mask {UINT64_C(0b0'00000'10000000'0000000000'0000000000'0000000000'0000000000'0000000000), UINT64_C(0)};

BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint64_t d128_inf_mask_high_bits {UINT64_C(0b0'11110'00000000'0000000000'0000000000'0000000000'0000000000'0000000000)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint64_t d128_nan_mask_high_bits {UINT64_C(0b0'11111'00000000'0000000000'0000000000'0000000000'0000000000'0000000000)};
BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint64_t d128_snan_mask_high_bits {UINT64_C(0b0'11111'10000000'0000000000'0000000000'0000000000'0000000000'0000000000)};

// Masks to update the significand based on the combination field
// In these first three 00, 01, or 10 are the leading 2 bits of the exp
// and the trailing 3 bits are to be concatenated onto the significand
Expand Down Expand Up @@ -1107,17 +1110,17 @@ constexpr auto signbit BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs)

constexpr auto isnan BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs) noexcept -> bool
{
return (rhs.bits_.high & detail::d128_nan_mask.high) == detail::d128_nan_mask.high;
return (rhs.bits_.high & detail::d128_nan_mask_high_bits) == detail::d128_nan_mask_high_bits;
}

constexpr auto isinf BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs) noexcept -> bool
{
return ((rhs.bits_.high & detail::d128_nan_mask.high) == detail::d128_inf_mask.high);
return (rhs.bits_.high & detail::d128_nan_mask_high_bits) == detail::d128_inf_mask_high_bits;
}

constexpr auto issignaling BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs) noexcept -> bool
{
return (rhs.bits_.high & detail::d128_snan_mask.high) == detail::d128_snan_mask.high;
return (rhs.bits_.high & detail::d128_snan_mask_high_bits) == detail::d128_snan_mask_high_bits;
}

constexpr auto isnormal BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs) noexcept -> bool
Expand All @@ -1136,12 +1139,12 @@ constexpr auto isnormal BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs

constexpr auto isfinite BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (decimal128 rhs) noexcept -> bool
{
return ((rhs.bits_.high & detail::d128_inf_mask_high_bits) != detail::d128_inf_mask_high_bits);
return (rhs.bits_.high & detail::d128_inf_mask_high_bits) != detail::d128_inf_mask_high_bits;
}

constexpr auto not_finite(decimal128 rhs) noexcept -> bool
{
return ((rhs.bits_.high & detail::d128_inf_mask_high_bits) == detail::d128_inf_mask_high_bits);
return (rhs.bits_.high & detail::d128_inf_mask_high_bits) == detail::d128_inf_mask_high_bits;
}

constexpr auto operator+(decimal128 rhs) noexcept -> decimal128
Expand Down Expand Up @@ -1206,27 +1209,21 @@ constexpr auto operator!=(Integer lhs, decimal128 rhs) noexcept
constexpr auto operator<(decimal128 lhs, decimal128 rhs) noexcept -> bool
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) || isnan(rhs) ||
(!lhs.isneg() && rhs.isneg()))
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())
{
return true;
}
else if (isfinite(lhs) && isinf(rhs))
{
return !rhs.isneg();
}
#else
if (!lhs.isneg() && rhs.isneg())
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())
if (not_finite(lhs) || not_finite(rhs))
{
return true;
if (isnan(lhs) || isnan(rhs) ||
(!lhs.isneg() && rhs.isneg()))
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())
{
return true;
}
else if (isfinite(lhs) && isinf(rhs))
{
return !rhs.isneg();
}
}
#endif

Expand Down
80 changes: 46 additions & 34 deletions include/boost/decimal/decimal128_fast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ namespace decimal {

namespace detail {

BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_inf = std::numeric_limits<uint128>::max() - 2;
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_qnan = std::numeric_limits<uint128>::max() - 1;
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_snan = std::numeric_limits<uint128>::max();
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_inf = uint128{UINT64_MAX - 2, UINT64_MAX};
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_qnan = uint128{UINT64_MAX - 1, UINT64_MAX};
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_snan = uint128{UINT64_MAX, UINT64_MAX};

BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_inf_high_bits = UINT64_MAX - 2;
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_qnan_high_bits = UINT64_MAX - 1;
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_snan_high_bits = UINT64_MAX;

struct decimal128_fast_components
{
Expand Down Expand Up @@ -460,17 +464,17 @@ constexpr auto signbit(decimal128_fast val) noexcept -> bool

constexpr auto isinf(decimal128_fast val) noexcept -> bool
{
return val.significand_ == detail::d128_fast_inf;
return val.significand_.high == detail::d128_fast_inf_high_bits;
}

constexpr auto isnan(decimal128_fast val) noexcept -> bool
{
return val.significand_ >= detail::d128_fast_qnan;
return val.significand_.high >= detail::d128_fast_qnan_high_bits;
}

constexpr auto issignaling(decimal128_fast val) noexcept -> bool
{
return val.significand_ == detail::d128_fast_snan;
return val.significand_.high == detail::d128_fast_snan_high_bits;
}

constexpr auto isnormal(decimal128_fast val) noexcept -> bool
Expand All @@ -485,12 +489,12 @@ constexpr auto isnormal(decimal128_fast val) noexcept -> bool

constexpr auto isfinite(decimal128_fast val) noexcept -> bool
{
return val.significand_ < detail::d128_fast_inf;
return val.significand_.high < detail::d128_fast_inf_high_bits;
}

constexpr auto not_finite(const decimal128_fast& val) noexcept -> bool
{
return val.significand_ >= detail::d128_fast_inf;
return val.significand_.high >= detail::d128_fast_inf_high_bits;
}

constexpr auto operator==(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool
Expand Down Expand Up @@ -542,40 +546,48 @@ constexpr auto operator!=(Integer lhs, decimal128_fast rhs) noexcept

constexpr auto operator<(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) || isnan(rhs) ||
(!lhs.isneg() && rhs.isneg()))
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())
{
return true;
}
else if (isfinite(lhs) && isinf(rhs))
{
return !signbit(rhs);
}
else if (isinf(lhs) && isfinite(rhs))
{
return signbit(rhs);
}
#else
if (!lhs.isneg() && rhs.isneg())
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())
#ifndef BOOST_DECIMAL_FAST_MATH
if (not_finite(lhs) || not_finite(rhs))
{
return true;
if (isnan(lhs) || isnan(rhs) ||
(!lhs.isneg() && rhs.isneg()))
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())
{
return true;
}
else if (isfinite(lhs) && isinf(rhs))
{
return !signbit(rhs);
}
else if (isinf(lhs) && isfinite(rhs))
{
return signbit(rhs);
}
}
#endif
#endif

// Needed to correctly compare signed and unsigned zeros
if (lhs.significand_ == 0 || rhs.significand_ == 0)
{
if (lhs.significand_ == 0 && rhs.significand_ == 0)
{
#ifndef BOOST_DECIMAL_FAST_MATH
return lhs.sign_ && !rhs.sign_;
#else
return false;
#endif
}
return lhs.significand_ == 0 ? !rhs.sign_ : lhs.sign_;
}

if (lhs.sign_ != rhs.sign_)
{
return lhs.sign_;
}

if (lhs.exponent_ != rhs.exponent_)
{
return lhs.sign_ ? lhs.exponent_ > rhs.exponent_ : lhs.exponent_ < rhs.exponent_;
Expand Down
12 changes: 6 additions & 6 deletions include/boost/decimal/detail/comparison.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ namespace decimal {
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,
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, bool>
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>
{
using comp_type = std::conditional_t<std::is_same<DecimalType, decimal32>::value, std::uint_fast32_t, std::uint_fast64_t>;
using comp_type = typename DecimalType::significand_type;

BOOST_DECIMAL_ASSERT(lhs_sig >= 0);
BOOST_DECIMAL_ASSERT(rhs_sig >= 0);
Expand Down Expand Up @@ -76,7 +76,7 @@ constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
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,
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), bool>
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>
{
using comp_type = std::conditional_t<(std::numeric_limits<T1>::digits10 > std::numeric_limits<T2>::digits10), T1, T2>;

Expand Down Expand Up @@ -170,9 +170,9 @@ 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, bool>
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>
{
using comp_type = std::uint_fast64_t;
using comp_type = std::conditional_t<std::is_same<DecimalType, decimal128>::value, detail::uint128, std::uint_fast64_t>;

BOOST_DECIMAL_ASSERT(lhs_sig >= 0);
BOOST_DECIMAL_ASSERT(rhs_sig >= 0);
Expand Down Expand Up @@ -204,7 +204,7 @@ constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
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), bool>
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>
{
using comp_type = std::conditional_t<(std::numeric_limits<T1>::digits10 > std::numeric_limits<T2>::digits10), T1, T2>;

Expand Down

0 comments on commit 43c44e6

Please sign in to comment.