Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Problem z mnożeniem typów float - z pozoru proste
Forum PHP.pl > Forum > PHP
memic
Witam,

ostatnio spotkałem się z dość dziwną sytuacją. Zrobiłem programik w PHP który zlicza mi pewne dane typu float z bazy mySQL. Operacje obliczeniowe wykonało samo zapytanie SQL-a.
Problem pojawia mi się dopiero wtedy gdy zaczynam mnożyć przez siebie liczby które mają np jedno miejsce po przecinku, np. gdy pomnoże:
2*5,3 to wynik jest 10,6
ale gdy pomnoże
3*5,3 to wynik jest 15,8999999999999998

zauważyłem że jeśli pomnożył bym 5,3 razy wielokrotność liczby 3 to również będzie zwracalo jakieś głupoty.

Przeciesz oczywiste jest że wynik powinien wyjść 15,9 a nie 15,899999998

Skąd te błędy? Zauważyłem również, że w javascript gdy mnoże dwie wartości z pól edit przez siebie to sytuacja jest identyczna.
Dla przykładu podam jeszcze, że np
3*5,4 = 16.200000000000003 - a nie jak powinno być 16,2.

Skąd się biorą te błędy i jak je wyeliminować?

z góry dzięki za odpowiedzi.
darko
w php wyeliminujesz poprzez zaokrąglenie
w js też
Nie będę ściemniał, że wiem z czego ta sytuacja wynika, choć przyznaję, że przychodzi mi na myśl zagadnienie dokładności (precyzji) wykonywania zmiennoprzecinkowych obliczeń arytmetycznych przez procesor komputera, ale być może w tym momencie wychodzę na idiotę...
skowron-line
@darko w JS to lepiej toFixed( param );
nmts
Cytat
jak je wyeliminować?


Może zamiana na typ double trochę skoryguje te wartości.
memic
Tak właśnie mam teraz zrobione, tzn. zmieniłem wszystkiem float na double i tak działa dobrze.
Dzięki double mogę przy pomocy sqla zadać takie zapytanie, żeby zwrócił mi sumę iloczynów. Gdy operacji było by dużo to przy float pojawił by się już taki błąd, że zaokrąglenie późniejsze w php nie przyniosło by efektu bo wynik mógłby się różnić od prawidłowej wartości.

zaokrąglanie - to jest dość odczywiste, ale nie rozwiązuje przyczyny problemu.

Myślę czy czasem nie ma jakiegoś parametru w samym serwerze apacha, który by poprawiał tę "wadę".

W javasripcie będę musiał wykorzystać zaokrąglanie bo chyba nic innego nie pozostaje.

Może faktycznie, procesor tak to dziwnie liczy smile.gif.

Dzięki za odpowiedzi, jeśli coś by się komuś jeszcze nasunęło na myśl dajcie znać. Może jest jakieś inne rozwiązanie podłoża problemu.

Pozdrawiam
Zyx
Nie da się tego wyeliminować. Jak niby wyobrażasz sobie dokładne zapisanie niepoliczalnie wielkiej ilości liczb rzeczywistych na skończonej, kilkudziesięciobitowej przestrzeni? Arytmetyka zmiennoprzecinkowa z definicji jest niedokładna. Każde działanie wykonywane w niej jest obarczone pewnym błędem. Właściwości:

* Działania zmiennoprzecinkowe nie są łączne. x + (y + z) nie musi być równe (x + y) + z
* Nie są rozdzielne: x* ( y + z ) != x * y + x * z

Ponadto w tej arytmetyce dokładnie da się zapisać jedynie ułamki o mianowniku będącym potęgą dwójki, np. 1/2 albo 3/16. Pozostałe mają rozwinięcia binarne nieskończone i nie da się ich dokładnie zapisać. W Twoim przypadku ułamek 5,3 nie ma mianownika będącego potęgą dwójki, zatem już na wstępie obarczony jest pewnym błędem, który się kumuluje podczas obliczeń.

Jeśli potrzebujesz precyzyjnych obliczeń (np. do obliczeń finansowych, które NIGDY nie powinny być robione w arytmetyce zmiennoprzecinkowej), musisz używać typów stałoprzecinkowych. W PHP ich nie ma, ale to nic nie szkodzi, gdyż można je prosto zasymulować, przekształcając je na zwykłe, całkowite.
wookieb
Można wyeliminować.

http://pl.php.net/manual/en/book.bc.php
thek
Jak wspomniano, wynika to z niedoskonałości zapisu danych dziesiętnych w systemie binarnym. Niektóre języki mają ten problem rozwiązany odpowiednią biblioteką (przykładowo C posiada bcd.h), ale ich użycie jest związane ze zwiększonym zapotrzebowaniem na zasoby. Jeśli jednak nie masz takiej możliwości to pozostaje jedynie zwiększanie dokładności. Z tego co pamiętam istnieje w php.ini zmienna związana z tym. Zrób Ctrl+F z frazą floating to znajdzie Ci liczbę określającą precyzję wykonywanych działań na liczbach zmiennoprzecinkowych. Domyślnie jest chyba 14
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.