2011-12-20 14 views
7

Wiem, że testowanie pływaków dla równości z powodu błędów precyzji jest niebezpieczne, ale czy można bezpiecznie testować zero? Mogę wymyślić kilka przypadków, na przykład w optymalizacji specjalnych przypadków w algorytmach, w których chciałbyś to zrobić. Pytanie dotyczy pływaków, ale przypuszczam, że odpowiedź dotyczy również podwójnych.Czy można bezpiecznie testować zmienną równą 0.0?

Rozważmy następujący kod:

float factor = calculateFactor(); 
if(factor != 0.0f) 
    applyComplexAlgorithm(factor); 
+0

Nie akceptuję odpowiedzi tylko ze względu na posiadanie zaakceptowanej odpowiedzi w moim pytaniu, tak naprawdę musi istnieć odpowiedź, na którą w pełni odpowiadam. –

Odpowiedz

8

Jest bezpieczny w tym sensie, że jeśli wartość jest ustawiona bezpośrednio do 0.0f, zwróci prawda tam.

NIE jest bezpieczne w tym sensie, że nie należy oczekiwać, że wartość wynikająca z obliczeń będzie dokładnie 0,0f.

Tak naprawdę używasz 0.0f jako specjalnej wartości magicznej, nie jako prawdziwego porównania z zero.

+0

Dobrze, to był zły przykład. W tym przypadku, tak naprawdę chciałem użyć 0.0f jako wartości specjalnej do porównania, czegoś, co byłoby jawnie ustawione. Nie wiem, dlaczego wymyśliłem ten przykład, ponieważ nie jest to nawet to, co próbuję zrobić. –

+0

Zrozumiano. Jednak unikaj wartości magicznych posypanych "normalnymi" wartościami. Zniszczają wykrywalność (tak jak w przypadku, gdy patrzysz na sygnaturę funkcji, nie jest oczywiste, że mówi ci coś innego niż czynnik), a także bardziej angażują się w testowanie (i używanie!) Interfejsu API. Zamiast tego, spróbuj zmienić, aby ten warunek został ujawniony jako inny interfejs API lub przynajmniej jest to oczywiste w wywołaniu API (te ostatnie są mniej użyteczne). – supermem613

5

Nie, to nie jest bezpieczne, ponieważ obliczenia w calculateFactor() nie będzie prawdopodobnie prowadzić do 0.0 nawet przez to arytmetycznie powinno. Trywialny przykład: (0.4-0.1) -0,3 kiedy odbywa się za pomocą double wyniki w 5.551115123125783e-17

3

to z pewnością bezpieczny, ale trzeba wziąć pod uwagę, co to znaczy dla algorytmów. Jeśli Twój algorytm używa factor jako dzielnika (i nie sprawdza samego dzielenia przez zero), to tak, przed wywołaniem factor != 0.0f należy sprawdzić, czy nie ma on wartości .

Teraz, niezależnie od tego, czy powinieneś sprawdzać wartość mniejszą niż niektóre epsilon przed użyciem factor, jest całkowicie zgodne z tym, co oznacza twój kod i nie można go ustalić w oderwaniu od dostarczonego kodu.

Jeśli (jak nawiązywałeś do innego komentarza) chcesz użyć specjalnej wartości 0.0f jako wartości wartownika, która oznacza coś konkretnego (np. Niezdolność do obliczenia współczynnika), to tak, jest to całkowicie bezpieczne do porównania przy użyciu ==. Na przykład, należy użyć następującego kodu z dnia 0.0f jest deterministyczny i nie podlega jakiejkolwiek błąd zaokrąglenia:

float calculateFactor() 
{ 
    int phase = moon_phase(); 
    if (phase == FULL) { // whatever special case requires returning 0.0f 
     return 0.0f; 
    } else { 
     return 1.0 + (phase * phase); // something that is never 0.0f 
    } 
} 

float factor = calculateFactor(); 
if(factor != 0.0f) 
    applyComplexAlgorithm(factor);