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
Nemělo by v té podmínce být <= ? pardon, nepřečetla jsem si hlášku, myslela jsem že ověřujete opačný stav.
ForestCZE
Profil
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
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
Pokud je na účtě 0.3, tak bys očekával rozdíl 0, ale on ty nejspíš vyjde -0.000000001 či něco podobného.
ForestCZE
Profil
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
Ne. 0.3 není "méně než 0.3". blbost. máte to dobře.
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
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)
Například takhle, s desetinnými čísly se pracuje se určitou tolerancí. Když jsou dostatečně blízká, považujeme je za shodné.
ForestCZE
Profil
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
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
Alphard:
Mám toho za sebou dost, ale nikdo není dokonalý. Jak to mám tedy řešit?
Davex
Profil
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
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";
        }
}

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: