How to check a double's bit pattern is 0x0 in a C++11 constexpr?
Asked Answered
E

4

9

I want to check that a given double/float variable has the actual bit pattern 0x0. Don't ask why, it's used in a function in Qt (qIsNull()) that I'd like to be constexpr.

The original code used a union:

union { double d; int64_t i; } u;
u.d = d;
return u.i == 0;

This doesn't work as a constexpr of course.

The next try was with reinterpret_cast:

return *reinterpret_cast<int64_t*>(&d) == 0;

But while that works as a constexpr in GCC 4.7, it fails (rightfully, b/c of pointer manipulation) in Clang 3.1.

The final idea was to go Alexandrescuesque and do this:

template <typename T1, typename T2>
union Converter {
    T1 t1;
    T2 t2;
    explicit constexpr Converter( T1 t1 ) : t1(t1) {}
    constexpr operator T2() const { return t2; }
};

// in qIsNull():
return Converter<double,int64_t>(d);

But that's not clever enough for Clang, either:

note: read of member 't2' of union with active member 't1' is not allowed in a constant expression
constexpr operator T2() const { return t2; }
                                       ^

Does anyone else have a good idea?

Esqueda answered 17/2, 2012 at 12:31 Comment(9)
I guess there are other bit patterns that also represent a floating point 0, that you don't want to find?Overstuff
There are exactly two bit patterns that represent 0. They are 000...000 and 100...000. The first bit is the sign bit. The second bit pattern is sometimes referred to as "negative zero".Rep
Perhaps return d == 0 && 1/d > 0;? (en.wikipedia.org/wiki/Signed_zero#Comparisons)Pressman
Don't ask why Don't tell me what to doLahdidah
@Lightness: don't tell Marc what not to do ;-pEpizootic
reinterpret_cast is prohibited to appear in constant expressions. But what if you attempt static_cast<const int64_t*>(static_cast<const void*>(&d)) instead?Strontianite
@LucDanton: I don't think it's reinterpret_cast vs. static_cast that's the issue, at least Clang rejects both. I guess it doesn't like taking the address. After all, how could it compute that at compile-time?Esqueda
@jdv-JandeVaan: cf. the original code. It tests for a literal 0x0 bitpattern, not FP-equals-zero. I do wonder why they're so keen on skipping -0, but as a first approximation, I want to maintain behavioural compatibility while turning that thing constexpr...Esqueda
@MarcMutz-mmutz Addressof is fine as a constant expression. It even appears in the (non-normative) examples: constexpr const int* addr(const int& ir) { return &ir; } // OK. reinterpret_cast, on the other hand, is explicitly forbidden. So obviously that's going to be a roadblock.Strontianite
E
6

I want to check that a given double/float variable has the actual bit pattern 0x0

But if it's constexpr then it's not checking any variable, it's checking the value that this variable is statically determined to hold. That's why you aren't supposed to pull pointer and union tricks, "officially" there isn't any memory to point at.

If you can persuade your implementation to do non-trapping IEEE division-by-zero, then you could do something like:

return (d == 0) && (1 / d > 0)

Only +/-0 are equal to 0. 1/-0 is -Inf, which isn't greater than 0. 1/+0 is +Inf, which is. But I don't know how to make that non-trapping arithmetic happen.

Epizootic answered 17/2, 2012 at 13:49 Comment(5)
Isn't the implementation usually non-trapping by default?Brecciate
@Christian: on GCC with vanilla command-line options I get a floating point error if I divide by zero.Epizootic
Aha, Ok. By the way, if I read correctly you can actually use feholdecxept from <cfenv> (C++11/C99) to enter non-trapping mode. And since he uses C++11, this might be a nice addition to your answer (but don't forget #pragma STDC FENV_ACCESS ON in this case).Brecciate
Ah forget my comment, I forgot were talking about compile-time here.Brecciate
Division by zero renders an expression non-constant in C++11.Relate
R
5

It seems both clang++ 3.0 and g++ 4.7 (but not 4.6) treats std::signbit as constexpr.

return x == 0 && std::signbit(x) == 0;
Relay answered 17/2, 2012 at 13:54 Comment(1)
Clang 3.0 doesn't implement constexpr? Anyway [c.math]/11 of C++11 clearly states that std::signbit isn't constexpr :(Esqueda
R
4

It is not possible to look at the underlying bit pattern of a double from within a constant expression. There was a defect in the C++11 standard which allowed such inspection by casting via a void*, but that was addressed by C++ core issue 1312.

As "proof", clang's constexpr implementation (which is considered to be complete) has no mechanism for extracting the representation of a constant double value (other than via non-standard vector operations, and even then there is currently no way to inspect the result).

As others have suggested, if you know you will be targeting a platform which uses IEEE-754 floating point, 0x0 corresponds to the value positive zero. I believe the only way to detect this, that works inside a constant expression in both clang and g++, is to use __builtin_copysign:

constexpr bool isPosZero(double d) {
  return d == 0.0 && __builtin_copysign(1.0, d) == 1.0;
}
Relate answered 2/3, 2012 at 6:6 Comment(0)
S
1

In c++20 one can use constexpr bit_cast, and so the check for the unsigned zero can be done like this:

std::bit_cast<uint64_t, double>(d) == 0

Stereochemistry answered 3/2, 2024 at 2:14 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.