Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] IF, less or equal, nie działa :/
Forum PHP.pl > Forum > Przedszkole
Tryllion
Witam

Mam taki kod i nie rozumiem co może być źle, jak znaleźć error sciana.gif

  1. IF( $val == $row->v ) {
  2. echo ' TAK';
  3. continue;
  4. }
  5. echo 'NIE'; continue;

ten if jest wywoływany w pętli while, nie rozumiem dlaczego ale czasem liczby o przecinkowej precyzji wcale nie są wykrywane jako równe exclamation.gif!
Co może być tego przyczyną ? Czy to nie są wartości typu int tylko String np. jak to sprawdzić, co może być źle ?

proszę o wskazówki bo naprawdę nie rozumiem
InosU31
do sprawdzenia jakiego typu jesli liczba badz jakas zmienna - bo chyba chcesz to sprawdzic - sluszy polecenie:

var_dump($zmienna);

a co do Twojego warunku to sprobuj tak :

  1. if( $val == $row->v ) {
  2. echo ' TAK';
  3.  
  4. }
  5. else echo 'NIE';


Pozdrawiam
thek
O błędzie w składni if-else nie piszę bo to już wytknięto. Co do liczb zmiennoprzecinkowych to niestety z porównywaniem ich trzeba mocno uważać. O ile nie są to jakieś, nazwijmy to, proste w stylu 0.5 czy mające ciut więcej miejsc po przecinku, to już porównywanie wyników bardziej skomplikowanych wyrażeń może dawać nierówne wartości z racji określonej dokładności liczb, a w związku z tym przy działaniach zachodzą co i rusz zaokrąglenia. Im ich więcej, tym większa szansa, że porównanie zakończy się jako FALSE. Dobrym przykładem by to zobrazować może być popularne:
1/9 * 9
Jak wiadomo 1/9 daje w zapisie dziesiętnym 0.11111111111111111111111111
przemnożenie przez 9 da więc:
0.9999999999999999999
a nie jak nakazuje matematyka 1 i takie właśnie pułapki czekają na programistów co i rusz w związku z zaokrągleniami i przybliżeniami.

EDIT: To co podałem jest proste i kompy takie trywialne przypadki zazwyczaj wyłapują, więc 1 ostatecznie wychodzi, chociaż wynika to z przekształceń wyrażeń arytmetycznych na niskim poziomie. Dzielenie jest zamieniane zazwyczaj na mnożenie przez odwrotność (ale to też zależy od choćby obecności koprocesora), ale o tym możesz nie wiedzieć, bo nie każdy programista musi znać architekturę komputerową włącznie z budową procesora i operacjami prze niego wykonywanymi smile.gif To miał być tylko przykład ilustrujący ideę, która leży u podstaw opisanego przez Ciebie błędu.
pheter
@Tryllion, bardzo słusznie zauważyłeś, że liczby zmiennoprzecinkowe nie są wykrywane jako równe, mimo iż liczby rzeczywiste które reprezentują są równe. Dokładnie to liczby zmiennoprzecinkowe w postaci binarnej stanowią reprezentację liczb rzeczywistych z pewną dokładnością. Nie znam dokładnie php i nie wiem czy są w tym języku jakieś specjalizowane funkcje rozwiązujące ten problem lecz możesz spróbować zmienić porównanie
  1. $val == $row->v
na np.
  1. epsilon = 1E-8;
  2. // epsilon to względnie mała liczba
  3. if ($val - $row->v < epsilon) {}
Tryllion
IF ma taką formę bo uciąłem wiele z niego i wkleiłem na forum to co istotne.

Dodałem (float) przy obu wartościach, nadal nie wykrywa, co ciekawe funkcja jest wywoływana w pętli i za pierwszym razem działa.
Przy drugim wywołaniu:
  1. FOR($i=1.0; $i<2; $i+=0.1) {
  2.  
  3. compare_va($i,$i+0.1); // tu się dzieją rózne rzeczy, i między innymi zostaje porównany argument 1 z innym wyciągniętym z bazy
  4.  
  5.  
  6. }
już nie działa, i wszystko jest NIE, za pierwszym równe elementy wykrywa, co może się zmienić w tym czasie, zapis zły ?

EDIT:
zmieniłem wartości na bardziej przystępne, w bazie są co 0.01 a pętla co 0.1 , teoretycznie co 10 powinno wykryć ale wykrywa tylko za 1 wywołaniem tej funkcji !?!
Np. 1.1 jest taki sam jak 1.1
ale już 1.2 nie z 1.2 ! nie rozumiem...
pheter
Nie wykrywa i wykrywać nie musi, bo 0.1 to wcale nie taka "okrągła" liczba jak się Tobie wydaje, a 0.01 tym bardziej winksmiley.jpg

Przeczytaj posty powyżej, powinno pomóc w zrozumieniu.
tua1
Jeśli zależy Ci na wysokiej precyzji zapoznaj się z:

http://www.php.net/manual/pl/ref.bc.php
thek
Cytat(pheter @ 28.10.2009, 21:34:01 ) *
Nie wykrywa i wykrywać nie musi, bo 0.1 to wcale nie taka "okrągła" liczba jak się Tobie wydaje, a 0.01 tym bardziej winksmiley.jpg

Przeczytaj posty powyżej, powinno pomóc w zrozumieniu.
To ja może wspomnę, że najlepsze pod względem "okrągłości" liczby w systemach komputerowych są dane łatwo zapisujące się binarnie, przykładowo 0.125 smile.gif Dlaczego? Zapiszcie sobie tę liczbę binarnie to zobaczycie winksmiley.jpg Dla leniwych wyjaśnię, że kolejne liczby po przecinku to coraz wyższe ułamki dwójki. Tak więc binarnie:
0.1(bin) = 2^-1 = 1/2^1 = 1/2 = 0.5(dec)
0.01(bin) = 2^-2 = 1/2^2 = 1/4 = 0.25(dec)
0.001(bin) = 2^-3 = 1/2^3 = 1/8 = 0.125(dec)
...i tak dalej smile.gif
Te liczby się dodają bez żadnych przybliżeń i można na nich liczyć bezpiecznie i to do ułamków dwójki są zaokrąglane liczby takie 0.75 to binarnie 0.11, ale już 0.76 to już będzie wariacja 0.110101... i dalej z precyzją lecąc jeszcze ileś zer i jedynek byleby jak najbliżej tego być. To sprawia, że dodając dwie takie liczby nie ma gwarancji dokładności. Są systemy, które to obchodzą i to w nich powinno się zapisywać dane jeśli chcesz mieć gwarancję dokładności. Poczytaj może nieco o BCD to Ci się rozjaśni temat. Inaczej zapisują one dane przez co są naprawdę dokładne, ale niestety kosztem zużywanej pamięci.
Tryllion
ostatecznie wkurzyłem się i dodałem do każdej ze zmiennej liczbowej .'X' , co jak rozumiem przekonwertowało obie wartości na string typu _,__X gdzie _ to cyfra, sztuczka zadziałała, ale i tak jest to rozwiązanie lamerskie, na szybko, ale myślę że do mojego projekciku wystarczy.

dzieki za odpowiedzi
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.