Autor | Zpráva | ||
---|---|---|---|
ForestCZE Profil |
Ahoj, mám v databázi sloupec ucet FLOAT(6,4)
A pak mám tento kód: <input type='checkbox' name='pole[]' value='hodnota' /> $neco = count($_POST["pole"]); $neco2 = $neco*0.1; if($_SESSION["ucet"] < $neco2) { echo "Nedostatek peněz!"; } Všechno je v pořádku, ale jakmile je výsledek 0.3, tak mi to vyhodí hlášku, že je nedostatek peněz i když je v proměnné $_SESSION["ucet"] 0.3 Nevěděl by někdo, proč to takhle blbne? Díky. |
||
Tori Profil |
<= ? |
||
ForestCZE Profil |
#3 · Zasláno: 15. 12. 2013, 20:10:18
Tori:
Taky jsem nad tím uvažoval, ale to je přeci nesmysl. Když něco stojí $0.3 a já budu mít přesně $0.3, tak přece splňuji podmínku. Pravda je, že jakmile do databáze dám 0.3001, tak už je to ok. Jenom nechápu, co je to za nesmysl. |
||
juriad Profil |
#4 · Zasláno: 15. 12. 2013, 20:16:51
Floating-Point Types (Approximate Value) - FLOAT, DOUBLE
Fixed-Point Types (Exact Value) - DECIMAL, NUMERIC Místo FLOAT(6,4) použíj DECIMAL(6,4). Uložení čísla jako FLOAT může dojít ke ztrátě přesnosti. Zkus si: echo $_SESSION["ucet"] - $neco2 |
||
ForestCZE Profil |
#5 · Zasláno: 15. 12. 2013, 20:25:19
juriad:
„echo $_SESSION["ucet"] - $neco2“ Toto mi vrátí: -5.5511151231258E-17 ... nechápu |
||
Tori Profil |
ForestCZE:
„Když něco stojí $0.3 a já budu mít přesně $0.3, tak přece splňuji podmínku“ |
||
juriad Profil |
ForestCZE:
Ano, zaokrouhlovací chyba. Aneb Časté potíže, zajímavosti a poučné debaty » Výsledkem výpočtu 1 - 0.9 není přesně 0.1
Buď nepočítej s desetinnými čísly vůbec, nebo uvědoměle (ohromně náročné), nebo vhodně zaokrouhli. Rozdíl zaokrouhli na tři desetinná čísla (první nenulová cifra je až na 17. místě) a porovnej s nulou. |
||
ForestCZE Profil |
#8 · Zasláno: 15. 12. 2013, 20:32:02
juriad:
Pomůže tedy změnt FLOAT na DECIMAL? |
||
juriad Profil |
Ne nepomůže (otestováno), omlouvám se za matení, není to způsobené databází.
echo 0.3 - 3 * 0.1; # -5.5511151231258E-17 |
||
ForestCZE Profil |
juriad:
„echo 0.3 - 3 * 0.1; # -5.5511151231258E-17“ Jenomže co mě mate, je: echo 0.1 - 1 * 0.1; # 0 echo 0.2 - 2 * 0.2; # 0 echo 0.3 - 3 * 0.1; # -5.5511151231258E-17 echo 0.4 - 4 * 0.4; # 0 . . . |
||
Alphard Profil |
Porovnání je na straně PHP a to s decimal pracovat neumí, zpětně přetypuje na klasickou plovoucí čárku.
ForestCZE: if(abs($_SESSION["ucet"] -$neco2) < 1e10) |
||
ForestCZE Profil |
#12 · Zasláno: 15. 12. 2013, 21:00:00
Alphard:
Děkuji za nápad. Koukněte ještě na můj předchozí příspěvek. V tom je celý ten "zakopaný pes" a moje hlava to nebere ... |
||
Alphard Profil |
#13 · Zasláno: 15. 12. 2013, 21:11:27
ForestCZE:
Radil vám už [#7] juriad, jen ne tak okatě. Vy asi nemáte nějaké hlubší vzdělání v této oblasti, že? Takže i pro všechny ostatní do budoucna stručné shrnutí: Float se ukládá jako znaménkový bit, mantisa*2^exponent (jestli máme exponent se znaménkovým bitem, nebo vždy odečítáme konstantu je teď už jedno). Je logické, že nelze každé číslo vyjádřit přesně, podobně jako 1/3 nelze přesně vyjádřit konečným desetinným číslem v desítkové soustavě, tj. 1/3 = 0.33333, ale potom na vyšší přesnosti 1/3 - 0.33333 != 0. Mluvíme o kvantování. Na příspěvku [#10] není nic divného, v zaokrouhlovacích chybách v tomto směru není žádné pravidlo (obecně samozřejmě ano). Některá čísla lze do binární soustavy převést přesně, některá ne a různé matematické operace pak chyby zhoršují, nebo částečně kompenzují. Nelze snadno předvídat, kdy nastane problém. |
||
ForestCZE Profil |
#14 · Zasláno: 15. 12. 2013, 21:18:13
Alphard:
Mám toho za sebou dost, ale nikdo není dokonalý. Jak to mám tedy řešit? |
||
Davex Profil |
#15 · Zasláno: 15. 12. 2013, 21:25:19
Ještě doplním Alpharda odkazy na nějakou tu nezbytnou teorii:
• Norma IEEE 754 a příbuzní: formáty plovoucí řádové tečky • Aritmetické operace s hodnotami ve formátu plovoucí řádové čárky ForestCZE: „Jak to mám tedy řešit?“ Zaokrouhluj nebo čísla blízké nule považuj za nulu. |
||
ForestCZE Profil |
#16 · Zasláno: 15. 12. 2013, 21:41:34
Davex:
„Zaokrouhluj nebo čísla blízké nule považuj za nulu.“ Tohle mi vyhodí 0, kterou já potřebuji: echo round(3*0.1-0.3); |
||
juriad Profil |
Ještě funkci round přidej druhý parametr (počet desetinných míst, která má zachovat), v tvém případě (zajímají tě centy, desetiny centů už ne) to bude 2:
echo round(3*0.1-0.3, 2); Pro přesvědčení, že žádné pěkné pravidlo pro zjištění, kdy chyba nastane, neexistuje: for($i = 0; $i < 10; $i++) { for($j = 0; $j <= $i; $j++) { $a = $i*0.1 - $j*0.1; $b = ($i-$j)*0.1; echo $i*0.1, ' - ', $j*0.1, $a == $b ? ' == ': ' != ', $b, "\n"; } } |
||
Časová prodleva: 10 let
|
0