2012-02-17 6 views
8

Chcę sprawdzić, czy dana zmienna double/float ma rzeczywisty wzorzec bitowy 0x0. Nie pytaj dlaczego, jest on używany w funkcji w Qt (qIsNull()), którą chciałbym być constexpr.Jak sprawdzić wzorzec bitów podwójnych to 0x0 w constexpr C++ 11?

Oryginalny kod używany do unii:

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

to nie działa jako constexpr oczywiście.

Kolejna próba była z reinterpret_cast:

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

Ale jednocześnie, że pracuje jako constexpr w GCC 4.7, nie powiedzie się (słusznie, b/c manipulacji wskaźnik) w Clang 3.1.

Ostatnim pomysłem było iść i zrobić to Alexandrescuesque:

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); 

Ale to nie jest wystarczająco mądry dla Clang, albo:

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

Czy ktoś ma dobry pomysł?

+1

Chyba są inne wzory bitowe, które również reprezentują zmiennoprzecinkowych 0, że don Chcesz znaleźć? –

+2

Istnieją dokładnie dwa wzory bitowe, które reprezentują 0. Są to 000 ... 000 i 100 ... 000. Pierwszy bit to bit znaku. Drugi wzór bitowy jest czasami określany jako "zero ujemne". – user763305

+2

Być może 'return d == 0 && 1/d> 0;'? (http://en.wikipedia.org/wiki/Signed_zero#Comparisons) –

Odpowiedz

6

Chcę sprawdzić, czy dana zmienna double/pływak ma rzeczywisty wzorzec bitowy 0x0

Ale jeśli to constexpr wtedy to nie sprawdzając zmienną, to sprawdzając wartość że ten zmienna jest statycznie określona do przechowywania. Dlatego nie powinno się ciągnąć za wskazówkami i sztuczkami związkowymi, "oficjalnie" nie ma na co zwracać uwagi.

Jeśli można przekonać implementacji zrobić zakaz odłowu IEEE dzielenie przez zero, wtedy można zrobić coś takiego:

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

Tylko +/-0 są równe 0. 1/-0 jest -Inf, które isn” t większa niż 0. 1/+0 to +Inf, która jest. Ale nie wiem, jak zrobić tę arytmetykę nie łapiąc pułapki.

+0

Czy implementacja nie jest zwykle domyślnie blokowana? –

+0

@Christian: w GCC z opcjami wiersza poleceń vanilla otrzymuję błąd zmiennoprzecinkowy, jeśli dzielę przez zero. –

+0

Aha, OK. Nawiasem mówiąc, jeśli dobrze przeczytam, możesz użyć 'feholdecxept' z' '(C++ 11/C99), aby przejść do trybu niewolnego. A ponieważ używa C++ 11, może to być miłym dodatkiem do twojej odpowiedzi (ale nie zapominaj o '#pragma STDC FENV_ACCESS ON' w tym przypadku). –

5

Wygląda na to, że zarówno clang ++ 3.0, jak i g ++ 4.7 (ale nie 4.6) traktują std::signbit jako constexpr.

return x == 0 && std::signbit(x) == 0; 
+1

Clang 3.0 nie implementuje 'constexpr'? W każdym razie [c.math]/11 z C++ 11 wyraźnie stwierdza, że ​​'std :: signbit' nie jest' constexpr' :( –

4

Nie jest możliwe, by spojrzeć na podstawowej bitów o double od wewnątrz stałej ekspresji. Wystąpiła usterka w standardzie C++ 11, która umożliwiła taką inspekcję przez odlewanie za pomocą void*, ale zostało to rozwiązane przez C++ core issue 1312.

Jako "dowód" implementacja klangu constexpr (która jest uważana za kompletną) nie ma mechanizmu do wyodrębniania reprezentacji stałej wartości double (innej niż za pomocą niestandardowych operacji wektorowych, a nawet wtedy nie ma obecnie możliwości aby sprawdzić wynik).

Zgodnie z sugestią innych osób, jeśli wiesz, że będziesz kierować na platformę, która używa punktu zmiennego IEEE-754, 0x0 odpowiada wartości dodatniej zero. Wierzę, że jedynym sposobem na wykrycie tego, który działa wewnątrz stałej ekspresji w obu szczęk i g ++, jest użycie __builtin_copysign:

constexpr bool isPosZero(double d) { 
    return d == 0.0 && __builtin_copysign(1.0, d) == 1.0; 
} 
Powiązane problemy