2010-10-12 15 views
6

Jeśli foo jest typu zmiennoprzecinkowego, czy poniższe wyrażenie jest prawidłowe/zalecane?Porównanie zmiennoprzecinkowe 0

(0.0f == foo * float(0)) 

Czy będzie miał oczekiwaną (matematyczną) wartość niezależnie od wartości foo?

Czy standard C++ definiuje zachowanie, czy jest to konkretna implementacja?

+0

spróbować go debugowania.. – rkellerm

+0

@ rursw1 Bardziej zainteresowany, jeśli istnieje gwarancja ze standardu – CsTamas

+0

@ rursw1: To nie zadziała. –

Odpowiedz

2

AFAIK, to niekoniecznie, może również zakończyć się bardzo blisko 0.

Jest ogólnie najlepiej porównać przeciwko epsilon. Używam funkcji takiej jak ta do takich porównań:

float EpsilonEqual(float a, float b, float epsilon) 
{ 
    return fabsf(a - b) < epsilon; 
} 
+0

@Armen: Masz rację, jego fabsf :) – Goz

+1

IEEE 754 nakazuje, że -0.0f == + 0.0f, nawet jeśli wartości bitowe są różne. –

+0

@Michael: Uczciwa. Nie umniejsza to jednak punktu. Jednak usunę odniesienie. – Goz

3

Po pierwsze nie jest to kwestia standardu C++. Raczej chodzi o model zmiennoprzecinkowy (najprawdopodobniej IEEE).

Dla IEEE float, to jest prawdopodobnie bezpieczne, ponieważ float(0) powinno dać taką samą liczbę jak 0.0f, a pomnożone przez jakikolwiek inny numer również powinno być 0.0f.

To, co nie jest bezpieczne, to wykonywanie innych operacji zmiennoprzecinkowych (np. Dodawanie i odejmowanie liczbami całkowitymi) i sprawdzanie ich pod kątem 0.0f.

+6

Pomnożenie dowolnej skończonej wartości IEEE przez zero da zero. Jeśli 'foo' jest nieskończonością lub NaN, to wynikiem mnożenia jest NaN, a wynik porównania jest fałszywy. –

+0

@Mike Seymour - Dobry punkt. Być może linia ta miała być sprawdzeniem, czy foo jest poprawną wartością zmiennoprzecinkową. –

1

z danym stwierdzeniem, możesz być całkiem pewny, że wynik będzie 0 i porównanie być true - Nie sądzę standardowe C++ rzeczywiście przepisuje, ale każdy rozsądny realizacja pływających rodzajów punktów będzie miał 0 pracować jak że.

Jednak dla większości innych obliczeń, wynik nie należy spodziewać się dokładnie równa dosłownym tego matematycznie poprawny wynik:

dlaczego nie moje numery, jak 0,1 + 0,2 dodatku do ładnej rundy 0.3 i zamiast tego otrzymuję dziwny wynik, taki jak 0.30000000000000004?

Ponieważ wewnętrznie korzystającym formatowanie (binarnie zmiennoprzecinkowych), który nie można dokładnie oznaczają liczbę jak 0,1, 0,2 lub 0,3 w ogóle.

Gdy kod jest kompilowany lub interpretować, twój „0.1” jest już zaokrąglona do najbliższej liczby w tym formacie , co skutkuje niewielką zaokrąglania błąd nawet przed obliczenie dzieje.

Przeczytaj The Floating-Point Guide, aby uzyskać szczegółowe objaśnienia i jak prawidłowo wykonać comparisons with expected values.

0

Właśnie przeczytałem ten artykuł w witrynie MSDN na temat opcji/fp w VisualStudio link text

optymalizacji ekspresji, które są nieważne dla wartości szczególnych (Nan, + nieskończoność, nieskończoność, +0, -0) nie będą dozwolone.Optymalizacje xx => 0, x * 0 => 0, x-0 => x, x + 0 => x i 0-x => -x są nieważne z różnych powodów (patrz IEEE 754 i standard C99 ).

+0

Zgaduję, że komentarz tutaj odnosi się tylko do faktu, że x * 0 jest NaN, jeśli x jest NaN. – hobbs

2

NaNs i Infinites mogą zepsuć takie porównania, o czym już wspominali inni.

Jednak jest jeszcze jedna pułapka: w C++ nie można polegać na wyrażeniu typu float w czasie kompilacji, porównywalnym równym temu samemu wyrażeniu ocenianemu w czasie wykonywania.

Powodem tego jest to, że C++ pozwala na rozszerzoną precyzję obliczeń fp, w dowolny sposób, chcąc nie chcąc. Przykład:

#include <iostream> 

// This provides sufficent obfuscation so that g++ doesn't just inline results. 
bool obfuscatedTrue() { return true; } 

int main() 
{ 
    using namespace std; 

    double const a = (obfuscatedTrue()? 3.0 : 0.3); 
    double const b = (obfuscatedTrue()? 7.0 : 0.7); 
    double const c = a/b; 

    cout << (c == a/b? "OK." : "\"Wrong\" comparision result.") << endl; 
} 

Wyniki z jednego konkretnego kompilatora:

C:\test> g++ --version | find "++" 
g++ (TDM-2 mingw32) 4.4.1 

C:\test> g++ fp_comparision_problem.cpp & a 
"Wrong" comparision result. 

C:\test> g++ -O fp_comparision_problem.cpp & a 
OK. 

C:\test> _ 

Cheers & HTH,

- Alf

+0

W jakiej wyższej precyzji obawiasz się, że wynik 'foo * float (0)' może się różnić od wyniku mnożenia dokonanego z dokładnością typu? –

+0

Czy jest jakaś przydatność dla kompilatorów do generowania wyników innych niż te, które zostałyby wytworzone przez wymuszenie operandów operatorów porównania na ich typach kompilacji (np. Pozwolenie by 'someFloat == otherFloat/3.0f' było ocenione tak, jakby było '(double) someFloat == (double) otherFloat/3.0')? Wydaje mi się, że w ogromnej większości przypadków, gdy ta ostatnia jest "szybsza", przyniosłoby to również bezużyteczne wyniki. – supercat

+0

@PascalCuoq: asnwer zawiera kompletny przykładowy plik wynikowy. tak więc myśl, że to jest coś, czego "boję się", że "może" się zdarzyć, zdaje się wskazywać, że nie przeczytałeś odpowiedzi. dlatego sugeruję, abyś to zrobił. –

Powiązane problemy