Viliam Búr

5. 8. 2008

Reálne čísla v jazyku Java

V jazyku Java máme dva primitívne typy určené na prácu s reálnymi číslami: float a double. Ich veľkosť je 4 alebo 8 bajtov, z toho je 1 bit znamienko, 8 alebo 11 bitov exponent, a 23 alebo 52 bitov mantisa. Reálne čísla sa zaokrúhľujú na určitý počet platných číslic, čiže čím väčšie číslo, tým väčšie zaokrúhlenie. Ak teda hovoríme o ich rozsahu, máme na mysli dve nezávislé veci: na akú presnosť sa hodnoty zaokrúhľujú, a aké sú najväčšie a najmenšie možné hodnoty.

Týmto primitívnym typom zodpovedajú dva objektové typy: java.lang.Float a java.lang.Double. Ich statické premenné "MIN_VALUE" a "MAX_VALUE" určujú najmenšie a najväčšie konečné kladné reálne čísla vyjadriteľné pomocou daného typu.

System.out.println("Float " + Float.MIN_VALUE + " " + Float.MAX_VALUE);
// Float 1.4E-45 3.4028235E38
System.out.println("Double " + Double.MIN_VALUE + " " + Double.MAX_VALUE);
// Double 4.9E-324 1.7976931348623157E308

Zápis "1.4E-45" znamená "1.4 × 10-45", čiže 0.000 000 000 000 000 000 000 000 000 000 000 000 000 000 001 4. Zápis "3.4028235E38" znamená "3.4028235 × 1038", čiže 340 282 350 000 000 000 000 000 000 000 000 000 000; záverečné nuly znamenajú, že číslice na týchto miestach sa zaokrúhľujú. Podobne zápisy "4.9E-324" a "1.7976931348623157E308" znamenajú "4.9 × 10-324" a "1.7976931348623157 × 10308", čo nebudem rozpisovať, pretože výsledné čísla majú vyše 300 číslic.

Čo sa stane, ak je výsledok výpočtu príliš veľké alebo príliš malé číslo, a nezmestí sa do daného rozsahu? Oba reálne typy majú pomocné konštanty, ktoré umožňujú vrátiť aspoň nejaké výsledky aj za hranicami rozsahu výpočtu. Príliš veľké kladné a záporné čísla vyjadrujeme pomocou konštánt "POSITIVE_INFINITY" a "NEGATIVE_INFINITY", doslova "kladné nekonečno" a "záporné nekonečno". Čísla príliš blízko nule sa zaokrúhľujú na nulu (existuje aj záporná nula "-0.0f", ktorá sa rovná obyčajnej nule, okrem špeciálnych situácií ako napríklad delenie nulou). Ak o výsledku nevieme povedať vôbec nič, napríklad nekonečno mínus nekonečno alebo nula delená nulou, vyjadrujeme to pomocou konštanty "NaN". (Na rozdiel od objektovej konštanty "null" sú tieto konštanty typu "float" alebo "double", takže môžu byť uložené aj do premennej primitívneho typu.)

Ak chceme zistiť, či náš výpočet vrátil jednu z takýchto hodnôt, použijeme metódy "isInfinite" a "isNaN".

Prečo sa pri reálnych číslach používajú takéto špeciálne konštanty, keď pri celých číslach nastane za podobných okolností obyčajné pretečenie alebo "ArithmeticException"? Jedným dôvodom je zaokrúhľovanie reálnych čísel. Ak pri výpočte s celými číslami delíme nulou, zvyčajne to znamená, že programátor zabudol ošetriť nejaký špeciálny prípad; je dobré, ak ho program na túto situáciu upozorní. Ak pri výpočte s reálnymi číslami delíme nulou, často to znamená, že sme chceli deliť nejakou nenulovou hodnotou, ktorá sa pri medzivýpočte zaokrúhlila na nulu. Ošetrovanie všetkých takýchto situácií by bolo náročné, a v podstate zbytočné. Dobrý programátor predsa vie, že pri práci s reálnymi číslami sa zaokrúhľuje, takže výsledok výpočtu je vždy do istej miery nespoľahlivý; záleží od konkrétneho vzorca, nakoľko môže zaokrúhľovacia chyba narásť. Druhým dôvodom je, že spôsob uloženia reálnych čísel v pamäti umožňuje medzi ne "prirodzeným" spôsobom doplniť tieto konštanty; v prípade celých čísel by bol podobný postup "umelý", a okrem iného by extrémne spomaľoval všetky programy.

(C) 2008 Viliam Búr viliam-bur.blogspot.com

System.out.println(Float.MAX_VALUE * 2);  // Infinity
System.out.println(Float.MIN_VALUE / 2);  // 0.0
System.out.println(0.0f /  0.0f);  // NaN
System.out.println(1.0f /  0.0f);  // Infinity
System.out.println(1.0f / -0.0f);  // -Infinity
System.out.println(1.0f / Float.POSITIVE_INFINITY);  //  0.0
System.out.println(1.0f / Float.NEGATIVE_INFINITY);  // -0.0

Základné matematické operácie robíme znamienkami "+" sčítanie, "-" odčítanie, "*" násobenie, "/" delenie. Ak pri výpočtoch používame celé aj reálne čísla, znamienko "/" medzi dvoma celými číslami znamená celočíselné delenie, medzi dvoma reálnymi alebo reálnym a celým znamená reálne delenie.

System.out.println(1    / 3   );  // 0
System.out.println(1    / 3.0f);  // 0.333...
System.out.println(1.0f / 3   );  // 0.333...
System.out.println(1.0f / 3.0f);  // 0.333...

Čísla môžeme porovnávať pomocou operátorov "==" rovná sa, "!=" nerovná sa, "<" je menšie, ">" je väčšie, "<=" je menšie alebo rovné, ">=" je väčšie alebo rovné. Počas výpočtu sa reálne čísla zaokrúhľujú, takže výsledok operátora "==" môže závisieť od zaokrúhľovania. Pri dvoch reálnych číslach zvyčajne nezisťujeme ich rovnosť, ale či je ich rozdiel v danej tolerancii.

float a = 123456789f;
float b = 123000123f;
float c = 987987987f;
float x = a * b * c;
float y = c * b * a;
System.out.println(x);  // 1.5002796E25
System.out.println(y);  // 1.5002795E25

Trieda java.lang.Math ponúka množstvo metód na výpočty s celými aj reálnymi číslami: absolútna hodnota "abs" a znamienko "signum"; väčšie alebo menšie z dvoch čísel "min" a "max"; zaokrúhľovanie "floor", "ceil", "round"; mocniny "exp" a "pow", odmocniny "sqrt" a "cbrt", a logaritmy "log" a "log10"; premena uhlov zo stupňov na radiány "toRadians" a naopak "toDegrees"; goniometrické funkcie "sin", "cos", "tan", "asin", "acos", "atan", "atan2" a hyperbolické funkcie "sinh", "cosh", "tanh". Poskytuje aj matematické konštanty "E" a "PI".

Existuje aj trieda java.lang.StrictMath, ktorá ponúka rovnaké metódy, ale navyše so zárukou, že na rôznych platformách dá rovnaký výpočet vždy celkom rovnaký výsledok.

Náhodné čísla získame pomocou objektu java.util.Random. Metódy "nextFloat" a "nextDouble" vracajú náhodné reálne čísla rovnomerne rozložené medzi 0 a 1. Metóda "nextGaussian" vracia náhodné reálne čísla podľa Gaussovho normálneho rozloženia s priemerom 0 a smerodajnou odchýlkou 1; to znamená, že približne 68% hodnôt nebude od priemeru ďalej (ľubovoľným smerom) ako smerodajná odchýlka, 95% hodnôt nebude ďalej ako dve smerodajné odchýlky, a 99.7% hodnôt nebude ďalej ako tri smerodajné odchýlky. Napríklad pri IQ testoch je priemer 100 a smerodajná odchýlka 15, takže tu je simulátor výsledkov IQ testov náhodných jedincov v populácii:

java.util.Random r = new java.util.Random();
for (int i = 0; i < 100; i++) {
  System.out.println(Math.round(100 + 15 * r.nextGaussian()));
}

Súvisiace články:

Menovky:

0 komentárov:

Zverejnenie komentára

Prihlásiť na odber Zverejniť komentáre [Atom]

<< Domov