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

Search the boards