logo

Zaokrouhlování chyb v Javě

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:

Java
public 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:

java
public 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:

Java
public 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.09999999999999998

Jak 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:
Java
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.1

Zde 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.

Java
public 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