Das Komprimieren vieler unendlicher reeller Zahlen in eine endliche Anzahl von Bits erfordert eine Näherungsdarstellung. Die meisten Programme speichern das Ergebnis von Ganzzahlberechnungen mit maximal 32 oder 64 Bit. Bei einer festen Anzahl von Bits ergeben die meisten Berechnungen mit reellen Zahlen Größen, die mit so vielen Bits nicht exakt dargestellt werden können. Daher muss das Ergebnis einer Gleitkommaberechnung häufig gerundet werden, damit es wieder in seine endliche Darstellung passt. Dieser Rundungsfehler ist ein charakteristisches Merkmal der Gleitkommaberechnung. Daher müssen wir bei Berechnungen in Gleitkommazahlen (insbesondere wenn es sich um Geldberechnungen handelt) auf Rundungsfehler in einer Programmiersprache achten. Sehen wir uns ein Beispiel an:
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); } }
String-Methoden
Ausgabe:
x = 0.7999999999999999
y = 0.8
false
Hier ist die Antwort nicht das, was wir erwartet hatten, da der Java-Compiler die Rundung vorgenommen hat.
Grund für den Rundungsfehler
Die Datentypen Float und Double implementieren die IEEE-Gleitkomma-754-Spezifikation. Das bedeutet, dass Zahlen in der folgenden Form dargestellt werden:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2was im Gleitkommaformat dargestellt wird als: 1,01 * 2^-3
Nicht alle Brüche können exakt als Bruch einer Zweierpotenz dargestellt werden. Als einfaches Beispiel 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 und kann daher nicht in einer Gleitkommavariablen gespeichert werden.
Ein weiteres Beispiel:
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); } }
Ausgabe:
x = 0.7999999999999999
y = 0.8
false
Ein weiteres Beispiel:
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); } }
Ausgabe:
a = 0.09999999999999998Wie behebt man Rundungsfehler?
- Runden Sie das Ergebnis: Die Round()-Funktion kann verwendet werden, um etwaige Auswirkungen der Speicherungenauigkeit der Gleitkommaarithmetik zu minimieren. Der Benutzer kann Zahlen auf die Anzahl der Dezimalstellen runden, die für die Berechnung erforderlich sind. Wenn Sie beispielsweise mit Währungen arbeiten, würden Sie wahrscheinlich auf zwei Dezimalstellen runden.
- Algorithmen und Funktionen: Verwenden Sie numerisch stabile Algorithmen oder entwerfen Sie Ihre eigenen Funktionen, um solche Fälle zu behandeln. Sie können Ziffern abschneiden/runden, deren Richtigkeit Sie nicht sicher sind (Sie können auch die numerische Genauigkeit von Operationen berechnen).
- BigDecimal-Klasse: Sie können die verwenden java.math.BigDecimal Klasse, die uns insbesondere bei großen Bruchzahlen Genauigkeit liefern soll. Das folgende Programm zeigt, wie der Fehler behoben werden kann:
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); } }
Ausgabe:
0.1Hier a = a.setScale(1 RoundingMode.HALF_UP);
Runden aauf 1 Dezimalstelle mit dem Rundungsmodus HALF_UP. Die Verwendung von BigDecimal bietet also eine präzisere Kontrolle über die Rechen- und Rundungsoperationen, was besonders für Finanzberechnungen oder andere Fälle, in denen Präzision von entscheidender Bedeutung ist, nützlich sein kann.
Wichtiger Hinweis:
Math.round rundet den Wert auf die nächste ganze Zahl. Da 0,10 näher bei 0 als bei 1 liegt, wird es auf 0 gerundet. Nach der Rundung und Division durch 1,0 ist das Ergebnis 0,0. Sie können also den Unterschied zwischen den Ausgaben mit der BigDecimal-Klasse und der Maths.round-Funktion erkennen.
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); } }
Ausgabe:
0.0