Posted By: Quasimodo (Quasimodo) on 'CZprogram'
Title: Re: Zaokrouhlovani cisla typu Extended
Date: Thu May 9 15:35:44 2002
> Ahoj.
>
> Ja mam obcas problemy s double/floaty. Zkomiluju to v debugovacim rezimu a
>
> vse jede ok. Pak 'release' a uz to pad a na division by zero, spatne se
> vyhodnoti podminka typu if ( (a-b) < 0) pro cisla, kde a = b a tak podobne.
> Ja to resim timhle:
>
> #define DOUBLE_PRECISION_CONSTANT 1e-6
>
> a
>
> if ( (a-b) < DOUBLE_PRECISION_CONSTANT) ....
>
>
> Je to hnus, ale zatim jsem tak zkrotil vsechny algoritmy, ktere mely
> problemy
> s podtekanim. Navic to funguje, i kdyz v prekladaci zadate optimalizaci :-)
Mladezi, tohle se mi nejak nezda... i kdyz uz jsem to v literature
podobne parkrat videl.
Chyba ve floatech je snad relativni a ne absolutni. Mam prece float zapsany
jako mantisa * exponent.
Takze, kdyz zavzpominam jak vypada pascal, lepsi porovnani bude vypadat nejak
takhle:
if (abs(a * (1 + 1e-5) >= (abs(b))) and (abs(a * (1 - 1e-5)) < abs(b)) then
rovna_se_to; Coz sice zafunguje dejme tomu pro stejne dobre porovnani cisel
1.000001e22 a 1.0e22 jako pro 1.0000001e-17 a 1.0 e-17, ale ma jako znacnou
nevyhodu, ze to nefunguje pokud a=0.0 presne a b treba 1e-30, coz bychom radi
prohlasili za shodu, pokud se doted pracovalo v radu rekneme jednotek. Tudiz
zde by bylo lepsi pracovat s onou absolutni konstantou, tak jak bylo uvedeno
vyse. Jenze rad hodnot s jakymi ma program pracovat nemusim vzdy znat.
(Stejnou rutinku bych rad pouzil kdyz budou mit na vstupu v jednotkach SI
pikofarady nebo gigawatty.)
Dalsi, alternativni, jeste o malicko lepsi reseni, ktere si hlida rad
promennych by bylo neco jako:
epsilon:= (abs(a)+abs(b)) / 2e5;
if abs(a-b) < epsilon then rovna_se_to;
Ovsem protoze si svoji matikou nejsem nikdy moc jisty, tak bud rad
vysledku odhadnu ze vstupnich parametru (programem) nebo pouzivam
priblizne tohle:
if ((abs(a + err) * (1 + 1e-5)) >= (abs(b + err) * (1 - 1e-5)))
and ((abs(a + err) * (1 - 1e-5)) <= (abs(b + err) * (1 + 1e-5)))
then rovna_se_to;
kde do err dosazuju bud minfloat nebo uzivatelsky zadanou toleranci.
Vsechny tyhle silene vyrazy jsou samozrejme trochu zkratit a optimalizovat,
vypisuju je takhle rozvlacne, aby bylo jednoduse patrne co delam :-)
Ten finalni vyraz muze po mensim zjednoduseni nakonec vypadat asi takhle:
if (a = 0.00) then a:=err;
if (b = 0.00) then b:=err;
if ((a * (1+1e-5)) > b) = ((a * (1-1e-5) < b) then rovna_se_to;
Tak a ted doufam, ze jsem toho moc nezvoral, jsem silene ospalej a boli me
hlava...
-
Clovek je nejpomalejsi zname zarizeni typu I/O.
Quasimodo