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.
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 *. –