Het comprimeren van veel oneindige reële getallen tot een eindig aantal bits vereist een benaderende weergave. De meeste programma's slaan het resultaat van berekeningen met gehele getallen op met maximaal 32 of 64 bits. Gegeven een vast aantal bits zullen de meeste berekeningen met reële getallen grootheden opleveren die met zoveel bits niet exact kunnen worden weergegeven. Daarom moet het resultaat van een drijvende-kommaberekening vaak worden afgerond om weer in de eindige weergave te passen. Deze afrondingsfout is een karakteristiek kenmerk van drijvende-kommaberekeningen. Daarom moeten we bij het verwerken van berekeningen in drijvende-kommagetallen (vooral als berekeningen in termen van geld zijn) rekening houden met afrondingsfouten in een programmeertaal. Laten we een voorbeeld bekijken:
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); } }
download youtube-video's op vlc
Uitgang:
x = 0.7999999999999999
y = 0.8
false
Hier is het antwoord niet wat we hadden verwacht, omdat het werd afgerond door de Java-compiler.
Reden achter afrondingsfout
Float- en Double-gegevenstypen implementeren de IEEE floating point 754-specificatie. Dit betekent dat getallen worden weergegeven in de vorm als:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2wat in drijvende-komma-indeling wordt weergegeven als: 1,01 * 2^-3
Niet alle breuken kunnen exact worden weergegeven als een fractie van een macht van twee. Als een eenvoudig voorbeeld 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 en kan dus niet worden opgeslagen in een variabele met drijvende komma.
Nog een voorbeeld:
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); } }
Uitgang:
x = 0.7999999999999999
y = 0.8
false
Nog een voorbeeld:
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); } }
Uitgang:
a = 0.09999999999999998Hoe afrondingsfouten corrigeren?
- Rond het resultaat af: De functie Round() kan worden gebruikt om eventuele effecten van onnauwkeurigheid van de drijvende-komma-opslag te minimaliseren. De gebruiker kan getallen afronden op het aantal decimalen dat nodig is voor de berekening. Wanneer u bijvoorbeeld met valuta werkt, rondt u waarschijnlijk af op twee decimalen.
- Algoritmen en functies: Gebruik numeriek stabiele algoritmen of ontwerp uw eigen functies om dergelijke gevallen aan te pakken. U kunt cijfers afkappen/ronden waarvan u niet zeker weet of ze correct zijn (u kunt ook de numerieke nauwkeurigheid van bewerkingen berekenen)
- BigDecimal-klasse: U kunt gebruik maken van de java.math.BigDecimal klasse die is ontworpen om ons nauwkeurigheid te geven, vooral in het geval van grote fractionele getallen. Het volgende programma laat zien hoe de fout kan worden verwijderd:
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); } }
Uitgang:
0.1Hier a = a.setScale(1 RoundingMode.HALF_UP);
Rondes aop 1 decimaal met de afrondingsmodus HALF_UP. Het gebruik van BigDecimal biedt dus een nauwkeurigere controle over de rekenkundige en afrondingsbewerkingen, wat vooral handig kan zijn voor financiële berekeningen of andere gevallen waarin precisie cruciaal is.
Belangrijke opmerking:
Math.round rondt de waarde af op het dichtstbijzijnde gehele getal. Omdat 0,10 dichter bij 0 ligt dan bij 1, wordt deze afgerond naar 0. Na afronding en deling door 1,0 is het resultaat 0,0. U kunt dus het verschil opmerken tussen de uitvoer met de BigDecimal-klasse en de Maths.round-functie.
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); } }
Uitgang:
0.0