I was watching "Using C++20 three way comparison - Jonathan Müller - Meeting C++ 2019" talk and it mentioned problems with classes that contain floating point members.
Problem comes from the fact that IEEE 754 comparisons involving NaN(s) are weird and do not provide total ordering. Talk gives a way to work around this problems, for example using strong_order or manually ignoring NaN values when implementing <=> (assuming that values are never NaN).
My questions is if there are some library wrappers that would enable me to say that "I promise" that my floats are never NaN or that would do slow but valid comparisons on floats(slower but safer since NaNs are now ordered). My goal is to avoid manual implementation of spaceship by making member float spaceship friendly(so I can default spaceship).
Using example from the talk:
// original class
struct Temperature{
double value;
};
struct TemperatureNoNan{
std::a_number<double> value; // I promise value will never be NaN
// Now spaceship defaulting works
};
struct TemperatureStrongO{
std::s_ordered<double> value; // I want strong ordering(2 diff NaNs are not the same)
// Now spaceship defaulting works
};
"I promise" that my floats are never NaN
template <std::floating_point T>
struct NeverNaN {
T val;
constexpr NeverNaN(T val) : val(val) { }
constexpr operator T() const { return val; }
constexpr bool operator==(NeverNaN const&) const = default;
constexpr std::strong_ordering operator<=>(NeverNaN const& rhs) const {
auto c = val <=> rhs.val;
assert(c != std::partial_ordering::unordered);
return c > 0 ? std::strong_ordering::greater :
c < 0 ? std::strong_ordering::less :
std::strong_ordering::equal;
}
};
Unfortunately, there's no good way to "lift" a comparison category like this. And it doesn't optimize very well at the moment.
that would do slow but valid comparisons on floats(slower but safer since NaNs are now ordered)
This one has specific library support by way of either std::strong_order()
or std::weak_order()
[cmp.alg] depending on what kind of comparison you want:
template <std::floating_point T>
struct TotallyOrdered {
T val;
constexpr TotallyOrdered(T val) : val(val) { }
constexpr operator T() const { return val; }
// depends on whether or not you want == NaN to still be false?
// might need to be: return (*this <=> rhs) == 0;
constexpr bool operator==(TotallyOrdered const&) const = default;
constexpr auto operator<=>(TotallyOrdered const& rhs) const {
return std::strong_order(val, rhs.val);
// ... or std::weak_order(val, rhs.val)
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With