logo

Zaokruživanje pogrešaka u Javi

Sažimanje mnogih beskonačnih realnih brojeva u konačan broj bitova zahtijeva aproksimativni prikaz. Većina programa pohranjuje rezultat cjelobrojnih izračuna na maksimalno 32 ili 64 bita. S obzirom na bilo koji fiksni broj bitova, većina izračuna sa stvarnim brojevima proizvest će količine koje se ne mogu točno prikazati korištenjem toliko bitova. Stoga se rezultat izračuna s pomičnim zarezom često mora zaokružiti kako bi se vratio u njegovu konačnu reprezentaciju. Ova pogreška zaokruživanja karakteristična je značajka računanja s pomičnim zarezom. Stoga dok rukujemo izračunima u brojevima s pomičnim zarezom (posebno ako se izračuni odnose na novac) moramo voditi računa o pogreškama zaokruživanja u programskom jeziku. Pogledajmo primjer:

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);  } } 


karta vs set

Izlaz:



x = 0.7999999999999999  
y = 0.8
false

Ovdje odgovor nije ono što smo očekivali jer je zaokruživanje koje je napravio java kompajler.

Razlog pogreške zaokruživanja

Tipovi podataka s pomičnim zarezom i Double implementiraju IEEE specifikaciju 754 s pomičnim zarezom. To znači da su brojevi predstavljeni u obliku kao što je:

SIGN FRACTION * 2 ^ EXP 

0,15625 = (0,00101)2što je u formatu pomičnog zareza predstavljeno kao: 1,01 * 2^-3
Ne mogu se svi razlomci točno prikazati kao razlomak potencije broja dva. Kao jednostavan primjer 0,1 = (0,000110011001100110011001100110011001100110011001100110011001… )2 i stoga se ne može pohraniti unutar varijable s pomičnim zarezom.

Još jedan primjer:

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);  } } 

Izlaz:

x = 0.7999999999999999  
y = 0.8
false

Još jedan primjer:

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);  } } 

Izlaz:

a = 0.09999999999999998

Kako ispraviti pogreške zaokruživanja?

  • Zaokružite rezultat: Funkcija Round() može se koristiti za minimiziranje učinaka netočnosti pohranjivanja aritmetike s pomičnim zarezom. Korisnik može zaokružiti brojeve na broj decimalnih mjesta koji je potreban za izračun. Na primjer, dok radite s valutom vjerojatno biste zaokružili na 2 decimalna mjesta.
  • Algoritmi i funkcije: Koristite numerički stabilne algoritme ili dizajnirajte vlastite funkcije za rješavanje takvih slučajeva. Možete skratiti/zaokružiti znamenke za koje niste sigurni da su točne (možete izračunati i numeričku preciznost operacija)
  • BigDecimal klasa: Možete koristiti java.math.BigDecimal klasa koja je osmišljena da nam pruži točnost, posebno u slučaju velikih frakcijskih brojeva. Sljedeći program pokazuje kako se greška može ukloniti:
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);  } } 


Izlaz:

0.1

Ovdje a = a.setScale(1 RoundingMode.HALF_UP);

runde ana 1 decimalno mjesto koristeći način zaokruživanja HALF_UP. Stoga korištenje BigDecimala pruža precizniju kontrolu nad aritmetičkim operacijama i operacijama zaokruživanja što može biti posebno korisno za financijske izračune ili druge slučajeve u kojima je preciznost ključna.

Važna napomena:

Math.round zaokružuje vrijednost na najbliži cijeli broj. Kako je 0,10 bliže 0 nego 1, zaokružuje se na 0. Nakon zaokruživanja i dijeljenja s 1,0 rezultat je 0,0. Dakle, možete primijetiti razliku između izlaza s BigDecimal klasom i funkcijom 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);  } } 

Izlaz:

0.0