Zhutnenie mnohých nekonečných reálnych čísel do konečného počtu bitov vyžaduje približnú reprezentáciu. Väčšina programov ukladá výsledok celočíselných výpočtov maximálne 32 alebo 64 bitov. Pri akomkoľvek pevnom počte bitov väčšina výpočtov s reálnymi číslami vytvorí množstvá, ktoré nemožno presne reprezentovať pomocou takého počtu bitov. Preto musí byť výsledok výpočtu s pohyblivou rádovou čiarkou často zaokrúhlený, aby sa zmestil späť do jeho konečnej reprezentácie. Táto chyba zaokrúhľovania je charakteristickým znakom výpočtu s pohyblivou rádovou čiarkou. Preto pri spracovávaní výpočtov v číslach s pohyblivou rádovou čiarkou (najmä ak ide o výpočty v peniazoch) sa musíme postarať o zaokrúhľovacie chyby v programovacom jazyku. Pozrime sa na príklad:
Javapublic class Main { public static void main(String[] args) { double a = 0.7; double b = 0.9; double x = a + 0.1; double y = b - 0.1; System.out.println('x = ' + x); System.out.println('y = ' + y ); System.out.println(x == y); } }
stránky java servera
výstup:
x = 0.7999999999999999
y = 0.8
false
Tu odpoveď nie je to, čo sme očakávali, dôvodom je zaokrúhlenie vykonané kompilátorom java.
Dôvod chyby zaokrúhlenia
Dátové typy Float a Double implementujú špecifikáciu IEEE s pohyblivou rádovou čiarkou 754. To znamená, že čísla sú reprezentované vo forme ako:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2ktorý je vo formáte s pohyblivou rádovou čiarkou reprezentovaný ako: 1,01 * 2^-3
Nie všetky zlomky môžu byť vyjadrené presne ako zlomok mocniny dvoch. Ako jednoduchý príklad 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 a preto ich nemožno uložiť do premennej s pohyblivou rádovou čiarkou.
Ďalší príklad:
javapublic class Main { public static void main(String[] args) { double a = 0.7; double b = 0.9; double x = a + 0.1; double y = b - 0.1; System.out.println('x = ' + x); System.out.println('y = ' + y ); System.out.println(x == y); } }
výstup:
x = 0.7999999999999999
y = 0.8
false
Ďalší príklad:
Javapublic class Main { public static void main(String args[]) { double a = 1.0; double b = 0.10; double x = 9 * b; a = a - (x); // Value of a is expected as 0.1 System.out.println('a = ' + a); } }
výstup:
a = 0.09999999999999998Ako opraviť chyby pri zaokrúhľovaní?
- Výsledok zaokrúhlite: Funkciu Round() možno použiť na minimalizáciu akýchkoľvek účinkov nepresnosti aritmetického ukladania s pohyblivou rádovou čiarkou. Používateľ môže zaokrúhliť čísla na počet desatinných miest, ktorý si vyžaduje výpočet. Napríklad pri práci s menou by ste pravdepodobne zaokrúhlili na 2 desatinné miesta.
- Algoritmy a funkcie: Na zvládnutie takýchto prípadov použite numericky stabilné algoritmy alebo navrhnite svoje vlastné funkcie. Môžete skrátiť/zaokrúhliť číslice, o ktorých si nie ste istí, či sú správne (môžete vypočítať aj číselnú presnosť operácií)
- Trieda BigDecimal: Môžete použiť java.math.BigDecimal triedy, ktorá je navrhnutá tak, aby nám poskytla presnosť najmä v prípade veľkých zlomkových čísel. Nasledujúci program ukazuje, ako možno chybu odstrániť:
import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String args[]) { BigDecimal a = new BigDecimal('1.0'); BigDecimal b = new BigDecimal('0.10'); BigDecimal x = b.multiply(new BigDecimal('9')); a = a.subtract(x); // Rounding to 1 decimal place a = a.setScale(1 RoundingMode.HALF_UP); System.out.println('a = ' + a); } }
výstup:
0.1Tu a = a.setScale(1 Režim zaokrúhľovania.HALF_UP);
Kola ana 1 desatinné miesto pomocou režimu zaokrúhľovania HALF_UP. Takže použitie BigDecimal poskytuje presnejšiu kontrolu nad aritmetickými a zaokrúhľovacími operáciami, ktoré môžu byť obzvlášť užitočné pri finančných výpočtoch alebo iných prípadoch, kde je presnosť rozhodujúca.
Dôležitá poznámka:
Math.round zaokrúhli hodnotu na najbližšie celé číslo. Keďže 0,10 je bližšie k 0 ako k 1, zaokrúhli sa na 0. Po zaokrúhlení a vydelení 1,0 je výsledok 0,0. Môžete si teda všimnúť rozdiel medzi výstupmi s triedou BigDecimal a funkciou Maths.round.
Javapublic class Main { public static void main(String args[]) { double a = 1.0; double b = 0.10; double x = 9 * b; a = a - (x); /* We use Math.round() function to round the answer to closest long then we multiply and divide by 1.0 to to set the decimal places to 1 place (this can be done according to the requirements.*/ System.out.println('a = ' + Math.round(a*1.0)/1.0); } }
výstup:
0.0