Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: php kilejność działań
Forum PHP.pl > Forum > PHP
mimol
Witam, niedawno miałem problem związany z tym, że PHP nie umie liczyć.
Mógłby ktoś wytłumaczyć mi na czym polega problem?
Wiem, że to ma związek z floating point i precyzją, ale to są PROSTE działania, max 3 miejsca po przecinku...
Załóżmy że mam taki kod
  1. $price = 24.5;
  2. $discountPrice = 7.35;
  3. $discount = $price - $discountPrice;
  4.  
  5.  
  6. $offerValue = 100 * $discount / $price;
  7. var_dump($offerValue); // zwraca ładnie 70
  8. var_dump((int)$offerValue); // zwraca 69 tutaj magia...
  9.  
  10. //idzemy więc dalej
  11. $offerValue = 100 * ($discount / $price);
  12. var_dump((int)$offerValue); // zwraca 70, zmiana kolejności wykonywania działań,


Proszę o podanie jakiegoś powodu dlaczego
  1. //discount / price = 0.7
  2. echo (int)(100 * ($discount / $price)); //70
  3. echo (int)(100 * $discount / $price); //69
  4. echo (int)($discount / $price * 100); //70


ew jak wyświetlić prawdziwą wartość $discount / $price (powinno być 0.7) a pewnie jest (6.9999999999999999999),
Oczywiście poradziłem sobie używając bcmath
com
no a czemu się dziwisz rzutujesz float na inta, a php to chyba już wszyscy wiedza nie radzi sobie z matematyką jak jego starsi bracia
Bo wynik masz taki:
Cytat
float(70)
int(69)
int(70)
mimol
Proszę czytać uważnie....

"Proszę o podanie jakiegoś powodu dlaczego" (zmiana kolejności wykonywania działań, zmienia liczbę)
com
no to Ci dałem powód, php przy którymś miejscu po przecinku się gubi, a ponieważ masz liczbę zmiennoprzecinkową a potem chcesz z niej zrobić całkowitą dlatego zwraca 69 zamiast 70 wink.gif

specjalnie wrzuciłem wynik żebyś zobaczył jakiego typy zwracany jest wynik wink.gif

  1. $price = 24.5;
  2. $discountPrice = 7.35;
  3. $discount = $price - $discountPrice;
  4.  
  5. echo 100 * $discount / $price; // zwróci 70
zegarek84
php jest dynamicznie rzutowany trochę dla wygody... wykonaj kilka operacji matematycznych na różnych typach liczb np. w c++ oraz wykonaj kilka rzutowan w trakcie, spróbuj zrozumieć gdzie tracisz precyzję to zrozumiesz gdzie problem i nie będziesz zaprzagal do obliczeń kobyly tam gdzie nie ma takiej potrzeby...
com
polecam ten materiał http://gynvael.coldwind.pl/?id=492 wink.gif
bostaf
Magia dzieje się już tutaj:
  1. $discount = $price - $discountPrice;

Zobacz, co wyświetla
  1. echo number_format($discount, 100);
  2. // wyświetli: 17.14999999999999857891452847979962825775146484375000000000000000000000000000000
    00000000000000000000000

Fajne, no nie? smile.gif
Wyjaśnienie tego zjawiska jest tutaj: http://docs.oracle.com/cd/E19957-01/806-35...g_goldberg.html (nie proś mnie o streszczenie wink.gif).
Sposób radzenia sobie z operacjami arytmetycznymi na liczbach zmiennoprzecinkowych tutaj: http://php.net/types.float (w czerwonej ramce).
Crozin
Cytat
Wiem, że to ma związek z floating point i precyzją, ale to są PROSTE działania, max 3 miejsca po przecinku...
Nie chodzi o liczbę cyfr po przecinku, a o możliwość ich zapisania bez utraty dokładności. I tak 0,1 zostanie zapisane z utratą danych, a 0,5 zostanie zapisane bez utraty danych mimo iż obie mają ledwie jedną cyfrę po przecinku.
Zmiana kolejności wykonywania działań sprawia, że inna ilość danych jest tracona na innym etapie co ma wpływ na końcowy wynik.
Cytat
Oczywiście poradziłem sobie używając bcmath
Niepotrzebnie zaprzęgasz tutaj BC Math, skorzystaj ze zwykłego round.
toffiak
Mały offtop odnośnie złego PHP które nawet nie potrafi poprawnie liczyć:

Python też "nie potrafi" liczyć poprawnie:

Kod
price = 24.5
discount_price = 7.35
discount = price - discount_price
offer_value = 100 * discount / price

print(offer_value)        # 69.999999999....
print(int(offer_value))   # 69

offer_value = 100 * ( discount / price )
print(int(offer_value))   # 70
mimol
  1. $price = 24.5;
  2. $discountPrice = 7.35;
  3. $discount = $price - $discountPrice;
  4.  
  5.  
  6. $offerValue = 100 * $discount / $price;
  7. echo number_format((100 * $discount / $price), 13); //70
  8. echo number_format((100 * $discount / $price), 14); //69.99

Ktoś wyjaśni skad to się bierze? Dlaczego 14 jest magiczna?
Crozin
Nie jest żadną magiczną liczbą. Popatrz sobie na dokładne wartości liczbowe i ilość dziewiątek po przecinku: http://ideone.com/Bfdmye
bostaf
Cytat(Crozin @ 2.01.2014, 22:34:42 ) *
Nie jest żadną magiczną liczbą. Popatrz sobie na dokładne wartości liczbowe i ilość dziewiątek po przecinku: http://ideone.com/Bfdmye

Dokładnie. To wynika z zaokrąglania pełnej wartości przechowywanej w pamięci. Nie wiem jaka dokładnie jest precyzja wartości przechowywanej w pamięci, ale przy 50 cyfrach po przecinku:
  1. number_format((100 * $discount / $price), 50);
  2. // 69.99999999999998578914528479799628257751464843750000

Zostawiając tylko 14 cyfr dostaniemy:
Kod
69.99999999999998

co zostanie wyświetlone z zaokrągleniem ostatniej 8 do 9, bo kolejna cyfra to 5 (czyli zaokrąglenie w górę).
Przy 13 miejscach po przecinku
Kod
69.9999999999999

co zostanie wyświetlone z zaokrągleniem ostatniej 9 do 0, bo kolejna cyfra to 8 (czyli zaokrąglenie w górę). Zamiana ostatniej cyfry znaczącej na 0 spowoduje łańcuch takich samych zaokrągleń powodujący wyświetlenie tej liczby jako 70,0000000000000.
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.