Sok végtelen valós szám véges számú bitre történő tömörítése közelítő ábrázolást igényel. A legtöbb program az egész számítások eredményét legfeljebb 32 vagy 64 biten tárolja. Bármilyen rögzített bitszám mellett a legtöbb valós számokkal végzett számítás olyan mennyiségeket eredményez, amelyeket nem lehet pontosan ábrázolni ennyi bittel. Ezért a lebegőpontos számítás eredményét gyakran le kell kerekíteni, hogy visszaférjen a véges reprezentációjába. Ez a kerekítési hiba a lebegőpontos számítások jellemző tulajdonsága. Ezért a lebegőpontos számokkal végzett számítások kezelésekor (különösen, ha a számításokat pénzben értjük) ügyelnünk kell a kerekítési hibákra egy programozási nyelvben. Lássunk egy példát:
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); } }
dinamikus tömb java-ban
Kimenet:
x = 0.7999999999999999
y = 0.8
false
Itt a válasz nem az, amit vártunk, a java fordító által végzett kerekítés miatt.
A kerekítési hiba oka
A lebegő és a kettős adattípusok az IEEE lebegőpontos 754 specifikációt valósítják meg. Ez azt jelenti, hogy a számok a következő formában vannak ábrázolva:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2amely lebegőpontos formátumban a következőképpen jelenik meg: 1.01 * 2^-3
Nem minden tört ábrázolható pontosan a kettő hatványának törtrészeként. Egyszerű példaként 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 és így nem tárolható lebegőpontos változón belül.
Egy másik példa:
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); } }
Kimenet:
x = 0.7999999999999999
y = 0.8
false
Egy másik példa:
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); } }
Kimenet:
a = 0.09999999999999998Hogyan lehet kijavítani a kerekítési hibákat?
- Az eredmény kerekítése: A Round() függvény használható a lebegőpontos aritmetikai tárolási pontatlanság hatásainak minimalizálására. A felhasználó a számokat a számításhoz szükséges tizedesjegyekre kerekítheti. Például, ha pénznemekkel dolgozik, valószínűleg 2 tizedesjegyre kerekít.
- Algoritmusok és funkciók: Használjon numerikusan stabil algoritmusokat, vagy tervezze meg saját függvényeit az ilyen esetek kezelésére. Levághatja/kerekítheti azokat a számjegyeket, amelyek helyességében nem biztos, hogy helyesek (a műveletek numerikus pontosságát is kiszámíthatja)
- BigDecimális osztály: Használhatja a java.math.BigDecimal osztály, amelyet arra terveztek, hogy pontosságot adjon nekünk, különösen nagy törtszámok esetén. A következő program megmutatja, hogyan lehet eltávolítani a hibát:
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); } }
Kimenet:
0.1Itt a = a.setScale(1 RoundingMode.HALF_UP);
Kerekek a1 tizedesjegyig a HALF_UP kerekítési mód használatával. Így a BigDecimal használata pontosabb ellenőrzést biztosít az aritmetikai és kerekítési műveletek felett, ami különösen hasznos lehet pénzügyi számításoknál vagy más olyan esetekben, ahol a pontosság kulcsfontosságú.
Fontos megjegyzés:
A Math.round az értéket a legközelebbi egész számra kerekíti. Mivel a 0,10 közelebb van a 0-hoz, mint az 1-hez, akkor 0-ra kerekítjük. A kerekítés és az 1,0-val való osztás után az eredmény 0,0. Így észreveheti a különbséget a BigDecimal osztály és a Maths.round függvény kimenetei között.
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); } }
Kimenet:
0.0