2009-10-22 8 views
13

Znów mamy inny problem z obliczaniem matematycznym.Obliczenia pływaka php 2 kropki dziesiętne

$a = 34.56 

$b = 34.55 

$a zrobić kilka obliczeń, aby uzyskać tę postać

$b robi zaokrąglania najbliższy 0,05 dostać liczba ta

co się stało jest

$c = $b - $a 

rzekomo Byłbym -0,01, ale echo, że jest show -0.00988888888888

próbuję użyć n umber_format($c, 2), ale wynik wynosi 0.00,

jak mogę się upewnić $a i $b jest dokładnie 2 miejsc po przecinku, bez ukrytych numer na plecach.

w moim php number_format wiedzy tylko w stanie sformatować wyświetlacz, ale wartość nie naprawdę 2 dziesiętny,

Mam nadzieję, że mogę uzyskać pomoc tutaj. To naprawdę mnie denerwowało.

+0

Zbadaj 'precyzję' w php.ini, aby uzyskać bardziej szczegółowe zmiany w całym PHP. Należy jednak pamiętać, że mamy do czynienia z * znaczącymi cyframi *. –

Odpowiedz

34

Spróbuj sprintf("%.2f", $c);

Liczby zmiennoprzecinkowe są reprezentowane w notacji IEEE na podstawie uprawnień 2, tak kończące liczby dziesiętne mogą nie być kończącą liczbę binarną, to dlaczego masz cyfry spływu.

Zgodnie z sugestią programisty długości zmiennej, jeśli znasz precyzję, której potrzebujesz i nie zmienia się (np. Gdy masz do czynienia z pieniędzmi), lepiej użyć po prostu stałych numerów punktów, np. Wyliczyć liczby jako centy zamiast dolarów

$a = 3456; 

$b = 3455; 

$c = $b - $a; 

sprintf ("%.2f", $c/100.0); 

W ten sposób nie będzie żadnych błędów zaokrąglania, jeśli wykonasz wiele obliczeń przed drukowaniem.

+0

dziękuję za odpowiedź, jedynym sposobem jest sformatowanie danych wyjściowych, nie można po prostu ustawić go na 2 dziesiętne, z wyjątkiem za pomocą ciągu. – Shiro

+0

Jeśli chcesz ustawić tylko 2 miejsca dziesiętne, powinieneś używać liczb stałych, a nie zmiennoprzecinkowych. Zobacz odpowiedź programisty zmiennej długości. –

+0

Nie do końca rozumiem, co oznacza liczba "ustalonego punktu", czy możesz podać przykład? Wielkie dzięki! – Shiro

12

Zastosowanie round():

$c = round($b - $a, 2); 

Uwaga: można także wybrać tryb zaokrąglania jako właściwe.

Edit: Ok ja nie rozumiem co robi sprintf() że number_format() nie jest:

$c = number_format($b - $a, 2); 

vs

$c = sprintf("%.2f", $b - $a); 

?

+0

Prawdopodobnie nic, ale sprintf() jest o wiele bardziej zabawny w użyciu dla facetów takich jak ja, którzy są przyzwyczajeni do C. Zapisuje również jeden zapis '$ foo = number_format ($ b - $ a, 2). "jabłka!" i zamiast tego piszą '$ foo = sprintf ("%. 2f jabłka! ", $ b - $ a)' - znacznie bardziej eleganckie, według mnie. –

+0

Okrągłe skały, może dlatego, że to wszystko, co wiem na razie, wciąż się uczę. – Newb

2

Wpadłeś w jedną z pułapek liczb zmiennoprzecinkowych; że nie zawsze mogą reprezentować ułamkowe ułamki dziesiętne. Jeśli chcesz dokładnych wartości dziesiętnych, lepiej jest używać liczb całkowitych, a następnie podzielić według precyzji na końcu.

Na przykład, jeśli wykonujesz obliczenia w elementach reprezentujących Dolary (lub twoją ulubioną walutę), możesz faktycznie wykonać obliczenia w centach całkowitych.

+0

czy możesz podać przykład jak działają centy z liczbami całkowitymi? Naprawdę nie rozumiesz, co masz na myśli. Wielkie dzięki za komentarz. – Shiro

+0

"Integer centów" oznacza, że ​​na przykład kwota 24,95 USD powinna być przechowywana nie jako zmiennoprzecinkowa 24,95, ale raczej jako liczba całkowita 2495. Kwota 10,00 USD zostanie zapisana jako liczba całkowita 1000. I tak dalej. –

2

Możesz bardzo zgrabnie ominąć wszystkie te problemy, korzystając z biblioteki bcmath.

Pamiętaj tylko, aby przeczytać dokumentację i uważać, czy podajesz argumenty jako ciągi znaków, czy jako typiczne typy danych.

4

Native Obliczenia:

$a = 34.56; 
$b = 34.55; 
$c = $b - $a; // -0.010000000000005  

działa zgodnie z oczekiwaniami (stosować zawsze funkcje BC dla rzeczywistych obliczeń numerycznych, sprawa jest dla wszystkich platform C opartych!):

$a = '34.56'; 
$b = '34.55'; 
$c = bcsub($b, $a, 4); // -0.0100  
+0

Aby kontynuować dalsze iteracje na ten temat. Użyj łańcuchów do reprezentowania danych, a nie typów danych zmiennoprzecinkowych/podwójnych. Następnie użyj funkcji BCMath do obliczania zmiennych. Aby użyć sprintf ("% 1 $ s", bcadd ($ var1, $ var2)); To samo dotyczy przechowywania danych w bazach danych. http://php.net/manual/en/book.bc.php – fyrye

3

I również ostatnio pojawił się w tym wydaniu podczas wykonywania obliczeń z użyciem elementów pływających. Na przykład miałem 2 pływaki, które po odjęciu i sformatowaniu miały wartość -0,00.

$floatOne = 267.58; 
$floatTwo = 267.58; 
$result = number_format($floatOne - floatTwo, 2); 
print $result; //printed a string -0.00 

Co zrobiłem było:

$result = abs($floatOne - $floatTwo);// Made the number positive 
print money_format('%i', $result); // printed the desired precision 0.00 

W moim rozwiązania wiem, że floatOne nigdy nie będzie mniejsza niż floatTwo. Funkcja money_format jest zdefiniowana tylko wtedy, gdy system ma funkcje strfmon, a system Windows nie.

1

Jeśli ktoś jeszcze dojdzie do tej strony z podobnymi problemami, odejmowanie liczb ruchomych powoduje błędy lub dziwne wartości. Chcę wyjaśnić ten problem z nieco większą ilością szczegółów. Winowajcą są liczby zmiennoprzecinkowe. Różne systemy operacyjne i różne wersje języków programowania mogą zachowywać się inaczej.

Aby zilustrować problem, wyjaśnię, dlaczego na prostym przykładzie poniżej.

Nie jest bezpośrednio związane z PHP i nie jest błędem. Jednak każdy programista powinien być świadomy tego problemu.

Ten problem zajął wiele żyć dwie dekady temu.

W dniu 25 lutego 1991 r. Problem związany z obliczaniem liczb zmiennoprzecinkowych w baterii rakiet Patriot MIM-104 uniemożliwił jej przechwycenie napływającego pocisku Scud w Dhahran, w Arabii Saudyjskiej, przyczyniającego się do śmierci 28 żołnierzy z 14. Oddziału Kwatermistrzostwa USA.

Ale dlaczego tak się dzieje?

Powodem jest to, że zmiennoprzecinkowe wartości reprezentują ograniczoną precyzję. Tak więc wartość może nie mieć tej samej reprezentacji ciągów po dowolnym przetwarzaniu. To także obejmuje zapisywanie wartości zmiennoprzecinkowej w twoim skrypcie i bezpośrednio drukowanie go bez żadnych operacji matematycznych.

Wystarczy prosty przykład:

$a = '36'; 
$b = '-35.99'; 
echo ($a + $b); 

Można by oczekiwać, że drukowanie 0,01, prawda? Ale wydrukuje bardzo dziwną odpowiedź, taką jak 0.009999999999998

Podobnie jak inne liczby, liczby zmiennoprzecinkowe podwójne lub zmiennoprzecinkowe są przechowywane w pamięci jako ciągi zer i jedynek.To, jak zmiennoprzecinkowe różni się od liczby całkowitej, polega na tym, że interpretujemy wartości 0 i 1, gdy chcemy na nie spojrzeć. Istnieje wiele standardów, w jaki sposób są przechowywane.

Liczby zmiennoprzecinkowe są zwykle pakowane w układzie odniesienia komputera jako bit znaku, pola wykładnik i mantysy lub mantysy, od lewej do prawej ....

dziesiętne liczby nie są dobrze reprezentowane w formacie binarnym z powodu braku wystarczającej ilości miejsca. Tak więc nie możesz wyrazić 1/3 dokładnie tak, jak to jest 0,33333333 ... prawda? Dlaczego nie możemy reprezentować 0,01 jako binarnej liczby zmiennoprzecinkowej z tego samego powodu. 1/100 jest 0,00000010100011110101110000 ..... z powtarzając 10100011110101110000.

Jeżeli 0,01 utrzymuje się w uproszczonej i systemowego obcięty postaci 01000111101011100001010 binarnie, gdy jest tłumaczone z powrotem po przecinku, byłoby czytać jak 0.0099999. ... w zależności od systemu (komputery 64-bitowe zapewniają znacznie lepszą precyzję niż 32-bitowe). W tym przypadku system operacyjny decyduje, czy wydrukować go w taki sposób, jak widzi, czy też uczynić go bardziej zrozumiałym dla człowieka. Zatem zależy to od maszyny, w jaki sposób chcą ją reprezentować. Ale może być chroniony na poziomie językowym różnymi metodami.

Jeśli sformatujesz wynik, echo number_format (0.009999999999998, 2); wydrukuje 0.01.

Dzieje się tak dlatego, ponieważ w tym przypadku trzeba poinstruować, jak należy go odczytać i jak precyzyjnie jest potrzebna.