2013-09-06 23 views
5

Mam tablicę asocjacyjną, której wartości są zmiennoprzecinkowe, które powinny być prawdopodobieństwem. Jako takie, mam podsumować je i wymagać, że wynik jest w rzeczywistości 1.Jakoś 1 nie równa się 1 (PHP)

$total = array_sum($array); 
echo '$total = '.$total."\n"; 
if ($total == 1) { 
    die("total is 1"); 
} else { 
    die("total is not 1"); 
} 

Ten tajemniczo Wyjścia:

$total = 1 
total is not 1 

robi var_dump($total) plony float(1), a jeszcze nawet $total == (float)1 zwraca false.

Co się dzieje?

+2

http://stackoverflow.com/questions/4682889/is-floating-point-ever-ok – thumbmunkeys

Odpowiedz

2

pływa w php (i innych języków) nie są precyzyjne, dlatego (float)1 może faktycznie być 1.0000000000000 lub .99999999999999823477

Zobacz odpowiedzieć PHP - Floating Number Precision więcej informacji

+0

Czy istnieje sposób, aby uzyskać php, aby powiedzieć mi, jaka jest jego rzeczywista wartość? – Mala

+0

'print (number_format ($ myNumber, 32);' powinien pozwolić ci zobaczyć z pewnym stopniem dokładności co to jest, ale o ile mi wiadomo, nie widzisz wartości _exact_. – Jordan

+0

whargbl "0.99999999999999988897769753748435" - nie miałem idea var_dump byłaby okrągła tak, jak – Mala

1

Obsada int robi

if ((int)$total == 1) 

I to zadziała :)

EDIT: lub nawet lepiej

$total = (int)array_sum($array); 
+1

byłoby to niefortunne z tym, co próbuję zrobić (patrz górna część pytania). Jeśli moje wartości zostały zmiażdżone - co ten kod jest do sprawdzenia - a suma faktycznie była 1.3, to przeszedłby test – Mala

+0

Rozumiem, przepraszam, że źle odczytałem twoje pytanie :( – Quillion

3

zmiennoprzecinkowych są z natury nieprecyzyjne i są bardzo rzadko równa się ze sobą ze względu na sposób ich przechowywania i błędów zaokrągleń. Powinieneś porównywać pływaki, sprawdzając, czy te dwie wartości są "wystarczająco blisko". To znaczy, porównując bezwzględną wartość różnicy między tymi dwoma wartościami do znacznie małego marginesu błędu (często określanego jako "epsilon").

Jedna taka implementacja może być:

if (abs($total - 1) < 0.000000001) 
    die("total is 1"); 
} else { 
    die("total is not 1"); 
} 

Należy zauważyć, że wymagania Twoja aplikacja może ustalić, co naprawdę bezpieczny margines błędu jest i na co punkt numery powinny być zaokrąglone na wyświetlaczu.


Jeśli masz do czynienia z wartościami walutowymi, na przykład, i wymagają dokładnej precyzji, lepszym rozwiązaniem byłoby zrezygnować z arytmetyki zmiennoprzecinkowej całkowicie. Jedną z opcji w tym przypadku byłoby użycie typu całkowitoliczbowego i zapisanie liczby jako centów, dzielenie tylko w ostatniej minucie, aby wyświetlić numer użytkownikowi (lub nawet nie dzieląc, i zamiast tego wprowadzając kropkę dziesiętną do łańcucha).

Powiązane problemy