![]() |
![]() ![]() |
![]() |
![]()
Post
#1
|
|
Grupa: Zarejestrowani Postów: 2 Pomógł: 0 Dołączył: 16.12.2009 Ostrzeżenie: (0%) ![]() ![]() |
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. |
|
|
![]()
Post
#2
|
|
Grupa: Zarejestrowani Postów: 2 885 Pomógł: 463 Dołączył: 3.10.2009 Skąd: Wrocław Ostrzeżenie: (0%) ![]() ![]() |
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ę... |
|
|
![]()
Post
#3
|
|
Grupa: Zarejestrowani Postów: 4 340 Pomógł: 542 Dołączył: 15.01.2006 Skąd: Olsztyn/Warszawa Ostrzeżenie: (0%) ![]() ![]() |
@darko w JS to lepiej toFixed( param );
|
|
|
![]()
Post
#4
|
|
Grupa: Zarejestrowani Postów: 283 Pomógł: 34 Dołączył: 21.03.2008 Ostrzeżenie: (0%) ![]() ![]() |
Cytat jak je wyeliminować? Może zamiana na typ double trochę skoryguje te wartości. Ten post edytował nmts 17.12.2009, 00:17:33 |
|
|
![]()
Post
#5
|
|
Grupa: Zarejestrowani Postów: 2 Pomógł: 0 Dołączył: 16.12.2009 Ostrzeżenie: (0%) ![]() ![]() |
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 (IMG:style_emoticons/default/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 |
|
|
![]()
Post
#6
|
|
Grupa: Zarejestrowani Postów: 952 Pomógł: 154 Dołączył: 20.01.2007 Skąd: /dev/oracle Ostrzeżenie: (0%) ![]() ![]() |
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. |
|
|
![]()
Post
#7
|
|
Grupa: Moderatorzy Postów: 8 989 Pomógł: 1550 Dołączył: 8.08.2008 Skąd: Słupsk/Gdańsk ![]() |
|
|
|
![]()
Post
#8
|
|
Grupa: Moderatorzy Postów: 4 362 Pomógł: 714 Dołączył: 12.02.2009 Skąd: Jak się położę tak leżę :D ![]() |
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
|
|
|
![]() ![]() |
![]() |
Aktualny czas: 3.10.2025 - 10:33 |