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

<compare>: Improve floating-point spaceship CPO codegen #1475

Merged
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
98 changes: 48 additions & 50 deletions stl/inc/compare
Original file line number Diff line number Diff line change
Expand Up @@ -396,35 +396,37 @@ namespace _Strong_order {
using _Floating_type = decay_t<_Ty1>;
using _Traits = _Floating_type_traits<_Floating_type>;
using _Uint_type = typename _Traits::_Uint_type;
using _Sint_type = make_signed_t<_Uint_type>;

auto _Left_uint = _STD bit_cast<_Uint_type>(_Left);
auto _Right_uint = _STD bit_cast<_Uint_type>(_Right);
const auto _Left_uint = _STD bit_cast<_Uint_type>(_Left);
const auto _Right_uint = _STD bit_cast<_Uint_type>(_Right);

// 1. Ultra-fast path: equal representations are equal.
if (_Left_uint == _Right_uint) {
return strong_ordering::equal;
}

// 2. Examine the sign bits.
const bool _Left_negative = (_Left_uint & _Traits::_Shifted_sign_mask) != 0;
const bool _Right_negative = (_Right_uint & _Traits::_Shifted_sign_mask) != 0;
const _Uint_type _Left_shifted_sign = _Left_uint & _Traits::_Shifted_sign_mask;
const _Uint_type _Right_shifted_sign = _Right_uint & _Traits::_Shifted_sign_mask;

// 3. Fast path: any negative value is less than any positive value.
if (_Left_negative != _Right_negative) {
return _Left_negative ? strong_ordering::less : strong_ordering::greater;
}
// 3. Interpret floating-point bit patterns as sign magnitude representations of integers,
// and then transform them into ones' complement representation.
// (Ones' complement representations of positive zero and negative zero are different.)
const _Uint_type _Left_sign = _Left_shifted_sign >> _Traits::_Sign_shift;
const _Uint_type _Right_sign = _Right_shifted_sign >> _Traits::_Sign_shift;

// 4. Clear the (identical) sign bits. We've already stored them for use at the end.
_Left_uint &= ~_Traits::_Shifted_sign_mask;
_Right_uint &= ~_Traits::_Shifted_sign_mask;
const _Uint_type _Left_xor = _Left_shifted_sign - _Left_sign;
const _Uint_type _Right_xor = _Right_shifted_sign - _Right_sign;

// 5. Perform the final comparison, reversed for negative values.
// (For example, 1.5 is less than 2.5, but -1.5 is greater than -2.5.)
if (_Left_negative) {
return _Right_uint <=> _Left_uint;
} else {
return _Left_uint <=> _Right_uint;
}
const _Uint_type _Left_ones_complement_uint = _Left_uint ^ _Left_xor;
const _Uint_type _Right_ones_complement_uint = _Right_uint ^ _Right_xor;

const auto _Left_ones_complement = static_cast<_Sint_type>(_Left_ones_complement_uint);
const auto _Right_ones_complement = static_cast<_Sint_type>(_Right_ones_complement_uint);

// 4. Perform the final comparison.
return _Left_ones_complement <=> _Right_ones_complement;
} else if constexpr (_Strat == _St::_Three) {
return static_cast<strong_ordering>(compare_three_way{}(_Left, _Right));
} else {
Expand Down Expand Up @@ -498,6 +500,7 @@ namespace _Weak_order {
using _Floating_type = decay_t<_Ty1>;
using _Traits = _Floating_type_traits<_Floating_type>;
using _Uint_type = typename _Traits::_Uint_type;
using _Sint_type = make_signed_t<_Uint_type>;

auto _Left_uint = _STD bit_cast<_Uint_type>(_Left);
auto _Right_uint = _STD bit_cast<_Uint_type>(_Right);
Expand All @@ -507,47 +510,42 @@ namespace _Weak_order {
return weak_ordering::equivalent;
}

// 2. Fold negative zero into positive zero.
constexpr _Uint_type _Negative_zero = _Traits::_Shifted_sign_mask;
// 2. Examine the sign bits.
const _Uint_type _Left_shifted_sign = _Left_uint & _Traits::_Shifted_sign_mask;
const _Uint_type _Right_shifted_sign = _Right_uint & _Traits::_Shifted_sign_mask;

if (_Left_uint == _Negative_zero) {
_Left_uint = 0;
} else if (_Right_uint == _Negative_zero) { // The representations are known to be non-equal here.
_Right_uint = 0;
}
// 3. Fold all NaN values together.
// (The fact that _Infinity_plus_one represents a signaling NaN is irrelevant here.)
constexpr _Uint_type _Infinity_plus_one = _Traits::_Shifted_exponent_mask + 1;

// 3. Examine the sign bits.
const bool _Left_negative = (_Left_uint & _Traits::_Shifted_sign_mask) != 0;
const bool _Right_negative = (_Right_uint & _Traits::_Shifted_sign_mask) != 0;
const _Uint_type _Left_magnitude = _Left_uint & ~_Traits::_Shifted_sign_mask;
const _Uint_type _Right_magnitude = _Right_uint & ~_Traits::_Shifted_sign_mask;

// 4. Fast path: after folding negative zero, any negative value is less than any positive value.
if (_Left_negative != _Right_negative) {
return _Left_negative ? weak_ordering::less : weak_ordering::greater;
if (_Left_magnitude > _Infinity_plus_one) {
_Left_uint = _Left_shifted_sign | _Infinity_plus_one;
}

// 5. Clear the (identical) sign bits. We've already stored them for use at the end.
_Left_uint &= ~_Traits::_Shifted_sign_mask;
_Right_uint &= ~_Traits::_Shifted_sign_mask;
if (_Right_magnitude > _Infinity_plus_one) {
_Right_uint = _Right_shifted_sign | _Infinity_plus_one;
}

// 6. Fold all NaN values together.
// (The fact that _Infinity_plus_one represents a signaling NaN is irrelevant here.)
constexpr _Uint_type _Infinity_plus_one = _Traits::_Shifted_exponent_mask + 1;
// 4. Interpret floating-point bit patterns as sign magnitude representations of integers,
// and then transform them into two's complement representation.
// (Two's complement representations of positive zero and negative zero are the same.)
const _Uint_type _Left_sign = _Left_shifted_sign >> _Traits::_Sign_shift;
const _Uint_type _Right_sign = _Right_shifted_sign >> _Traits::_Sign_shift;

if (_Left_uint > _Infinity_plus_one) {
_Left_uint = _Infinity_plus_one;
}
const _Uint_type _Left_xor = _Left_shifted_sign - _Left_sign;
const _Uint_type _Right_xor = _Right_shifted_sign - _Right_sign;

if (_Right_uint > _Infinity_plus_one) {
_Right_uint = _Infinity_plus_one;
}
const _Uint_type _Left_twos_complement_uint = (_Left_uint ^ _Left_xor) + _Left_sign;
const _Uint_type _Right_twos_complement_uint = (_Right_uint ^ _Right_xor) + _Right_sign;

// 7. Perform the final comparison, reversed for negative values.
// (For example, 1.5 is less than 2.5, but -1.5 is greater than -2.5.)
if (_Left_negative) {
return static_cast<weak_ordering>(_Right_uint <=> _Left_uint);
} else {
return static_cast<weak_ordering>(_Left_uint <=> _Right_uint);
}
const auto _Left_twos_complement = static_cast<_Sint_type>(_Left_twos_complement_uint);
const auto _Right_twos_complement = static_cast<_Sint_type>(_Right_twos_complement_uint);

// 5. Perform the final comparison.
return static_cast<weak_ordering>(_Left_twos_complement <=> _Right_twos_complement);
} else if constexpr (_Strat == _St::_Three) {
return static_cast<weak_ordering>(compare_three_way{}(_Left, _Right));
} else if constexpr (_Strat == _St::_Strong) {
Expand Down
20 changes: 20 additions & 0 deletions tests/std/tests/P0768R1_spaceship_cpos/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,11 @@ constexpr void test_floating() {
{10, bit_cast<float>(0xFFFFFFFFu)}, // negative quiet NaN, all payload bits set
{10, bit_cast<float>(0xFFC01234u)}, // negative quiet NaN, some payload bits set
{10, bit_cast<float>(0xFFC00000u)}, // negative quiet NaN, no payload bits set
#ifdef __clang__ // TRANSITION, MSVC "quiets" signaling NaNs into quiet NaNs when constant evaluated
{10, bit_cast<float>(0xFFBFFFFFu)}, // negative signaling NaN, all payload bits set
{10, bit_cast<float>(0xFF801234u)}, // negative signaling NaN, some payload bits set
{10, bit_cast<float>(0xFF800001u)}, // negative signaling NaN, minimum payload bits set
#endif // defined(__clang__)
{20, -numeric_limits<float>::infinity()}, // negative infinity
{30, -0x1.fffffep+127f}, // negative max normal
{31, -0x1.000000p-126f}, // negative min normal
Expand All @@ -459,6 +464,11 @@ constexpr void test_floating() {
{70, 0x1.000000p-126f}, // min normal
{71, 0x1.fffffep+127f}, // max normal
{80, numeric_limits<float>::infinity()}, // infinity
#ifdef __clang__ // TRANSITION, MSVC "quiets" signaling NaNs into quiet NaNs when constant evaluated
{90, bit_cast<float>(0x7F800001u)}, // signaling NaN, minimum payload bits set
{90, bit_cast<float>(0x7F801234u)}, // signaling NaN, some payload bits set
{90, bit_cast<float>(0x7FBFFFFFu)}, // signaling NaN, all payload bits set
#endif // defined(__clang__)
{90, bit_cast<float>(0x7FC00000u)}, // quiet NaN, no payload bits set
{90, bit_cast<float>(0x7FC01234u)}, // quiet NaN, some payload bits set
{90, bit_cast<float>(0x7FFFFFFFu)}, // quiet NaN, all payload bits set
Expand All @@ -470,6 +480,11 @@ constexpr void test_floating() {
{10, bit_cast<Floating>(0xFFFFFFFFFFFFFFFFull)}, // negative quiet NaN, all payload bits set
{10, bit_cast<Floating>(0xFFF8000000001234ull)}, // negative quiet NaN, some payload bits set
{10, bit_cast<Floating>(0xFFF8000000000000ull)}, // negative quiet NaN, no payload bits set
#ifdef __clang__ // TRANSITION, MSVC "quiets" signaling NaNs into quiet NaNs when constant evaluated
{10, bit_cast<Floating>(0xFFF7FFFFFFFFFFFFull)}, // negative signaling NaN, all payload bits set
{10, bit_cast<Floating>(0xFFF0000000001234ull)}, // negative signaling NaN, some payload bits set
{10, bit_cast<Floating>(0xFFF0000000000001ull)}, // negative signaling NaN, minimum payload bits set
#endif // defined(__clang__)
{20, -numeric_limits<Floating>::infinity()}, // negative infinity
{30, -0x1.fffffffffffffp+1023}, // negative max normal
{31, -0x1.0000000000000p-1022}, // negative min normal
Expand All @@ -482,6 +497,11 @@ constexpr void test_floating() {
{70, 0x1.0000000000000p-1022}, // min normal
{71, 0x1.fffffffffffffp+1023}, // max normal
{80, numeric_limits<Floating>::infinity()}, // infinity
#ifdef __clang__ // TRANSITION, MSVC "quiets" signaling NaNs into quiet NaNs when constant evaluated
{90, bit_cast<Floating>(0x7FF0000000000001ull)}, // signaling NaN, minimum payload bits set
{90, bit_cast<Floating>(0x7FF0000000001234ull)}, // signaling NaN, some payload bits set
{90, bit_cast<Floating>(0x7FF7FFFFFFFFFFFFull)}, // signaling NaN, all payload bits set
#endif // defined(__clang__)
{90, bit_cast<Floating>(0x7FF8000000000000ull)}, // quiet NaN, no payload bits set
{90, bit_cast<Floating>(0x7FF8000000001234ull)}, // quiet NaN, some payload bits set
{90, bit_cast<Floating>(0x7FFFFFFFFFFFFFFFull)}, // quiet NaN, all payload bits set
Expand Down