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:
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); } }
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:
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); } }
Izlaz:
x = 0.7999999999999999
y = 0.8
false
Još jedan primjer:
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); } }
Izlaz:
a = 0.09999999999999998Kako 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:
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.1Ovdje 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.
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); } }
Izlaz:
0.0