Is there a way to write a type trait to determine whether a type supports negative zero in C++ (including integer representations such as sign-and-magnitude)? I don't see anything that directly does that, and std::signbit
doesn't appear to be constexpr
.
To clarify: I'm asking because I want to know whether this is possible, regardless of what the use case might be, if any.
The best one can do is to rule out the possibility of signed zero at compile time, but never be completely positive about its existence at compile time. The C++ standard goes a long way to prevent checking binary representation at compile time:
reinterpret_cast<char*>(&value)
is forbidden in constexpr
.union
types to circumvent the above rule in constexpr
is also forbidden.1/0.0 != 1/-0.0
is out of the question.The only thing one can test is if the domain of an integer type is dense enough to rule-out signed zero:
template<typename T>
constexpr bool test_possible_signed_zero()
{
using limits = std::numeric_limits<T>;
if constexpr (std::is_fundamental_v<T> &&
limits::is_exact &&
limits::is_integer) {
auto low = limits::min();
auto high = limits::max();
T carry = 1;
// This is one of the simplest ways to check that
// the max() - min() + 1 == 2 ** bits
// without stepping out into undefined behavior.
for (auto bits = limits::digits ; bits > 0 ; --bits) {
auto adder = low % 2 + high %2 + carry;
if (adder % 2 != 0) return true;
carry = adder / 2;
low /= 2;
high /= 2;
}
return false;
} else {
return true;
}
}
template <typename T>
class is_possible_signed_zero:
public std::integral_constant<bool, test_possible_signed_zero<T>()>
{};
template <typename T>
constexpr bool is_possible_signed_zero_v = is_possible_signed_zero<T>::value;
It is only guaranteed that if this trait returns false then no signed zero is possible. This assurance is very weak, but I can't see any stronger assurance. Also, it says nothing constructive about floating point types. I could not find any reasonable way to test floating point types.