Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Złe zaokrąglanie
Forum PHP.pl > Forum > PHP
msulik
Witam. Nie siedzę w bugach php ani innych takich wynalazkach, więc może robię wiele hałasu o nic.

Mianowicie mamy sobie liczbę netto * vat = brutto, tzn 371.311475 * 1.22 = 452.9999995 (szesc dziewiatek i piątka). Zaokrąglenie do 6 miejsc po przecinku powinno dac 453. I tak sie prawie dzieje:
  1. <?php
  2. echo round(452.9999995, 6); // Daje 453
  3.  
  4. // Ale juz np.
  5. echo round(371.311475 * 1.22, 6);
  6. // powinno dawac teoretycznie to samo, czyz nie?
  7.  
  8. // Czesciowe rozwiazanie tego problemu, to dodanie 001 na koncu liczby netto:
  9. echo round(371.311475001 * 1.22, 6); // i teraz jest ok - daje 453
  10. ?>


Podobny problem z tymi zerami na koncu mam w MySQL:
  1. SELECT round(371.311475 * 1.22, 6), round(371.311475000001 * 1.22, 6)

To daje nam:
452.999999 | 453

Na chlopski rozum to bez sensu. W szkole uczyli mnie, że jak się zaokrągla do N miejsc po przecinku, to patrzy się nie dalej jak N+1.

Czy ktos moze mi to wyjasnic jak pastuch krowie? smile.gif
Seth
Wyglada na to, ze to nie jest blad php:

http://binarychoice.pl/_images/vs_round.gif
biggrin.gif
NuLL
Hehe - to tak zawsze jest z VAT-em.
Marusz
No dobra... ale co powiecie na to:
  1. <?php
  2.  
  3. $vA = 452.9999995;
  4. $vB = 371.311475;
  5. $vV = 1.22;
  6.  
  7. $vZ = $vB*$vV;
  8.  
  9. echo $vA . "n";
  10. echo $vZ . "n";
  11.  
  12. if ( $vA == $vZ )
  13. {
  14.  
  15. echo "OKn";
  16.  
  17. }
  18. else 
  19. {
  20.  
  21. echo "AMOKn";
  22.  
  23. }
  24.  
  25. ?>


Output:
Cytat
452.9999995
452.9999995
AMOK
NuLL
A czy liczby porównywane zgadzają się binarnie questionmark.gif snitch.gif Bo tu mogą być ucięte.
dr_bonzo
Pewnie jest to zwiazane ze sposobem przechowywania liczb przez php

  1. <?php
  2. print( round( round(371.311475 * 1.22, 7), 6 ) . '<br />' );
  3. ?>

^^^Wydaje sie dzialac.
NuLL
Pewnie tak smile.gif

Gdyby wykorzystać funkcje kalkulatora binarnego wyniki byłyby dobre.
popo
liczby rzeczywiste sa przechowywane (nie tylko w php) jako cecha i mantysa co w polaczeniu z ograniczona dlugoscia slowa binarnego (zalezne od jezyka i procesora) powstaja bledy zaokraglen i nie jest to wada php tylko tego ze komputery pracuja w systemie binarnym o ograniczonej precyzji obliczen jesli moge cos zasugerowac to przy liczbach z okreslonego przedzialu mozna zapisywac je w bazie jako staloprzecinkowe typ DECIMAL powinno toto nieco polepszyc sytuacje co do php to
Cytat
Precyzja liczb zmiennoprzecinkowych

Jest oczywiste, że nawet proste ułamki dziesiętne, takie jak 0.1 lub 0.7, nie mogą zostać skonwertowane na ich dwójkowe odpowiedniki bez niewielkiej straty dokładności. Może to powodować pewne problemy: na przykład wyrażenie floor((0.1+0.7)*10) zwykle ma wartość 7, zamiast oczekiwanej 8, gdyż wewnętrzna reprezentacja tego wartości to liczba 7.9999999999....

Powiązane jest to z faktem, że dla pewnych ułamków zwykłych nie istnieje skończone rozwinięcie dziesiętne. Na przykład 1/3 w reprezentacji dziesiętnej ma wartość 0.3333333. . ..

Dlatego nigdy nie należy wierzyć liczbom zmiennoprzecinkowym do ostatniej cyfry i nigdy nie należy wykonywać operacji porównania na stwierdzenie równości. Do operacji na liczbach zmiennoprzecinkowych o naprawdę dużej precyzji należy użyć biblioteki BCMath lub funkcji gmp.

cytat z manuala smile.gif
http://php.net.pl/manual/pl/language.types...float-precision
TomASS
Zgadza się, komputer (a właściwie system) przetrzymuje liczby zmiennoprzecinkowe (i nie tylko) jako pewne, bardzo dokładne (ale zawsze niedokładne tongue.gif ) PRZYBLIŻENIE i z tąd mogą pochodzić te błędy.
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.