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?

Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
  • 1
    I guess there are other bit patterns that also represent a floating point 0, that you don't want to find? –  Feb 17 '12 at 12:44
  • 2
    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". – Johan Råde Feb 17 '12 at 13:28
  • 2
    Perhaps `return d == 0 && 1/d > 0;`? (http://en.wikipedia.org/wiki/Signed_zero#Comparisons) – Alan Stokes Feb 17 '12 at 13:47
  • `Don't ask why` Don't tell me what to do – Lightness Races in Orbit Feb 17 '12 at 13:54
  • 3
    @Lightness: don't tell Marc what not to do ;-p – Steve Jessop Feb 17 '12 at 13:56
  • `reinterpret_cast` is prohibited to appear in constant expressions. But what if you attempt `static_cast(static_cast(&d))` instead? – Luc Danton Feb 17 '12 at 13:56
  • 1
    @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? – Marc Mutz - mmutz Feb 17 '12 at 15:41
  • @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`... – Marc Mutz - mmutz Feb 17 '12 at 15:49
  • @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. – Luc Danton Feb 17 '12 at 17:41

3 Answers3

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.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
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;
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
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;
}
Richard Smith
  • 13,696
  • 56
  • 78