Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: floor źle działa
Forum PHP.pl > Forum > PHP
php programmer
Oto kod
  1. <?php
  2. echo $liczba = 7/0.14;
  3. echo '<br />';
  4. echo floor($liczba);
  5. ?>


Który wyświetla mi
50
49

Jakoby zaokrągleniem w dół liczby 50 było 49!
Czy ktoś wie dlaczego mi tak robi i jak to ominąć.
cicik
W sumie dziwne bo jak za $liczba podstawi się 50 to jest OK.
Podejrzewam, że na naszych niedoskonałych procesorach 7/0.14 = 49.999999999999999999 (to tylko przykład). Zwykłe wyświetlanie wyniku pewnie zaokrągla na którymś miejscu po przecinku i wychodzi 50. Natomiast floor pewnie bierze całą liczbę i zaokrągla w dół. Aczkolwiek sprawa ciekawa. W wolnej chwili zobaczę jak to wygląda w innych językach i kompilatorach.
grzemach
a musisz koniecznie wykorzystywać floor? nie mozesz np spróbować round()?
php programmer
round to nie floor,
ale chyba potraktuje to strpos i substr
cicik
Kod C#, kompilator VS .Net 2005, procesor Intel Centrino, system Windows XP Professional

  1. <?php
  2. double liczba = 7./ 0.14;
  3. Console.WriteLine(liczba);
  4. Console.WriteLine(Math.Floor(liczba));
  5. ?>


Wynik:
  1. <?php
  2. 50
  3. 49
  4. ?>


Kod C++, kompilator VS .Net 2005, procesor Intel Centrino, system Windows XP Professional

  1. <?php
  2. double liczba;
  3. liczba = 7./ 0.14;
  4. cout << liczba << endl;
  5. cout << floor(liczba) << endl;
  6. ?>


Wynik:
  1. <?php
  2. 50
  3. 49
  4. ?>


Pod Demianem pracującym na AMD Athlonie XP 2500+ kod w C++ daje taki sam zły wynik.
Grzyw
Analogie to powyższego przykładu spotkałem w literaturze swego czasu. Błąd faktycznie bierze się z niedoskonałości liczenia procesorów, jest nie do wyeliminowania drogą bezpośrednią, wynik zawsze będzie błędny.
cicik
Pewnie kluczem do rozwiązania zagadki jest to, że liczba 0.1 jest liczbą niewymierną w systemie dwójkowym, a 0.14 = 0.1 + 0.04.
Procesory stosują różne triki, żeby tę cechę wyeliminować ale jak widać nie zawsze działa.

Aczkolwiek kiedyś miałem taki problem, że pisałem program do obliczania jakimi monetami wydać resztę w sklepie (na jakiś konkurs to było). Chodziło to aby badać reszty z dzielenia. No i generalnie na Intelach wyniki dzielenia (albo zaokrąglania - to dawno było) były inne od tych uzyskanych na AMD - tym samym kodem źródłowym!!! To mnie wtedy dość mocno zaskoczyło bo procesory przechodzą bardzo ostre testy na poprawność działania jednostki arytmetycznej, zwłaszcza tej zmiennoprzecinkowej. Historia o tym jak kiedyś wybuchła jakaś rakieta kosmiczna bo któryś Pentium źle liczył jest dość znana.
likemandrake
Problem polega wlasnie na tym co mowil moj poprzednik, aby ten temat sobie blizej przyblizyc, nalezy poczytac o tym, jak binarnie sa kodowane liczby po przecinku i mowa tu o liczbach zmiennoprzecinkowych. Cala sprawa kreci sie wtedy wokol kodowania FP2.

Na szybko mowiac, dla przykladu mamy taki ciag binarny (podzielony na grupy):

1|101|100101

liczba w pierwszej grupie jest to znak (+/-)
liczby w drugiej jest to cecha, inaczej zakres liczby
liczby w trzeciej grupie to mantysa, jest to tak naprawde nasza liczba, tyle ze zamieniona tak, zeby byla po przecinku

Jest wzór, wg którego obliczamy (odkodowujemy) liczbę zawartą w tym kodzie, ale ze względu na użyte potęgi, nie podałem go tu, a więc zainteresowanych odsyłam do google.pl

Chodzi o to, ze im mamy część całkowitą dalszą od zera, tym jej wartość po przecinku staje sie mniej dokladna i coraz bardziej zaokraglana wraz ze zwiększaniem tej odległości.

Co do problemu postawionego w tym poście, mam rozumieć, że w obu przypadkach chcemy uzyskać liczbę 50?
Kosmi
Cytat(php programmer @ 8.08.2007, 08:35:09 ) *
Oto kod
  1. <?php
  2. echo $liczba = 7/0.14;
  3. echo '<br />';
  4. echo floor($liczba);
  5. ?>


Który wyświetla mi
50
49

Jakoby zaokrągleniem w dół liczby 50 było 49!
Czy ktoś wie dlaczego mi tak robi i jak to ominąć.


Na szybko może tak:

  1. <?php
  2. echo $liczba = round(7/0.14,6);
  3. echo '<br />';
  4. echo floor($liczba);
  5. ?>


Wynik:
50
50
Musiałbyś jedynie dobrze dobrać dokładność zaokrąglenia.
Ludvik
To nie ma sensu. Liczba 50.6 zostanie zaokrąglona do 51, a nie o to chodzi.

php programmer: Próbowałeś rzutowania na integer? Rzuć okiem, ja w tej chwili nie mam jak tego sprawdzić...
Kosmi
Cytat(Ludvik @ 8.08.2007, 11:24:02 ) *
To nie ma sensu. Liczba 50.6 zostanie zaokrąglona do 51, a nie o to chodzi.

php programmer: Próbowałeś rzutowania na integer? Rzuć okiem, ja w tej chwili nie mam jak tego sprawdzić...


  1. <?php
  2. echo $liczba = round(50.6,6);
  3. echo '<br />';
  4. echo floor($liczba);
  5. ?>


Wynik:
50.6
50

Przez to pisałem o dokładności zaokrąglenia.

Pozdrawiam
Kosmi
akubiczek
Cytat(Ludvik @ 8.08.2007, 11:24:02 ) *
To nie ma sensu. Liczba 50.6 zostanie zaokrąglona do 51, a nie o to chodzi.

Jakie 50.6? Tam jest zaokrąglanie z dokładnością do 6 miejsc po przecinku. I wydaje się to być dobrym pomysłem.

Poza tym dodam jeszcze od siebie - zamienić dzielenie na mnożenie:

  1. <?php
  2. echo $liczba = 7*(1/0.14);
  3. echo '<br />';
  4. echo floor($liczba);
  5. ?>


50
50
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.