Zhutnění mnoha nekonečných reálných čísel do konečného počtu bitů vyžaduje přibližnou reprezentaci. Většina programů ukládá výsledek celočíselných výpočtů při max. 32 nebo 64 bitech. Vzhledem k jakémukoli pevnému počtu bitů bude většina výpočtů s reálnými čísly produkovat množství, která nelze přesně reprezentovat pomocí takového množství bitů. Proto musí být výsledek výpočtu s plovoucí desetinnou čárkou často zaokrouhlen, aby se vešel zpět do své konečné reprezentace. Tato zaokrouhlovací chyba je charakteristickým rysem výpočtu s plovoucí desetinnou čárkou. Proto při zpracování výpočtů v číslech s pohyblivou řádovou čárkou (zejména pokud jsou výpočty v penězích) se musíme postarat o zaokrouhlovací chyby v programovacím jazyce. Podívejme se na pří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); } }
junit testovací případy
výstup:
x = 0.7999999999999999
y = 0.8
false
Zde odpověď není to, co jsme očekávali, důvodem je zaokrouhlení provedené kompilátorem Java.
Důvod chyby zaokrouhlení
Datové typy Float a Double implementují specifikaci IEEE 754 s pohyblivou řádovou čárkou. To znamená, že čísla jsou reprezentována ve formě jako:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2který ve formátu s plovoucí desetinnou čárkou je reprezentován jako: 1,01 * 2^-3
Ne všechny zlomky lze přesně vyjádřit jako zlomek mocniny dvou. Jako jednoduchý příklad 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 a nelze je tedy uložit do proměnné s plovoucí desetinnou čárkou.
Další pří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
Další pří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.09999999999999998Jak opravit zaokrouhlovací chyby?
- Zaokrouhlete výsledek: Funkci Round() lze použít k minimalizaci jakýchkoli účinků nepřesnosti aritmetického úložiště s pohyblivou řádovou čárkou. Uživatel může zaokrouhlit čísla na počet desetinných míst, který vyžaduje výpočet. Například při práci s měnou byste pravděpodobně zaokrouhlili na 2 desetinná místa.
- Algoritmy a funkce: Použijte numericky stabilní algoritmy nebo navrhněte své vlastní funkce pro řešení takových případů. Můžete zkrátit/zaokrouhlit číslice, u kterých si nejste jisti, že jsou správné (můžete také vypočítat numerickou přesnost operací)
- Velká desítková třída: Můžete použít java.math.BigDecimal třídy, která je navržena tak, aby nám poskytla přesnost zejména v případě velkých zlomkových čísel. Následující program ukazuje, jak lze chybu odstranit:
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.1Zde a = a.setScale(1 Režim zaokrouhlení.POLOVINA_UP);
kola ana 1 desetinné místo pomocí režimu zaokrouhlování HALF_UP. Použití BigDecimal tedy poskytuje přesnější kontrolu nad aritmetickými a zaokrouhlovacími operacemi, což může být zvláště užitečné pro finanční výpočty nebo jiné případy, kde je přesnost rozhodující.
Důležitá poznámka:
Math.round zaokrouhlí hodnotu na nejbližší celé číslo. Protože 0,10 je blíže 0 než 1, zaokrouhlí se na 0. Po zaokrouhlení a dělení 1,0 je výsledek 0,0. Můžete si tedy všimnout rozdílu mezi výstupy s třídou BigDecimal a funkcí 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