Konfrontieren Sie Java-Gleitkommafehler

** Der Code in diesem Artikel ist in Java SE 8 geschrieben. ** **.

Hintergrund

Wenn Sie Variablen vom Typ Double mit == vergleichen, werden Sie darauf hingewiesen, dass dies mit statischen Analysewerkzeugen nicht gut ist, sondern ein Fehler ist. Ich war neugierig auf verschiedene Dinge und versuchte (jetzt) herauszufinden, wie man mit Gleitkommazahlen umgeht.

Fazit zuerst

Wenn Sie sich nicht für die Leistung interessieren, verwenden Sie Big Decimal. Wenn Sie das Design der Datenbank ändern können, können Sie die Ziffern verschieben und mit int oder long behandeln.

Problem

Bitte schauen Sie sich zunächst diesen an.

Code

	double doubleValue1 = 1.0d;
	double doubleValue2 = 0.9d;
	double resultValue = doubleValue1 - doubleValue2;

	System.out.println("doubleValue1: " + doubleValue1);
	System.out.println("doubleValue2: " +  doubleValue2);
	System.out.println(resultValue == 0.1d);
	System.out.println("Subtraktionsergebnis: " + resultValue);

** Ausführungsergebnis **

	doubleValue1: 1.0
	doubleValue2: 0.9
	false
Subtraktionsergebnis: 0.09999999999999998

Das Subtraktionsergebnis soll großartig sein.

Computer verarbeiten Zahlen binär.

Einige Brüche, wie beispielsweise "0,9", werden jedoch zu kreisförmigen Brüchen, wenn sie binär ausgedrückt werden. Da der Computer nur eine endliche Anzahl von Ziffern verarbeiten kann, wird er auf einen geeigneten Wert gerundet und es tritt ein Fehler auf.

Da es subtil mit einem anderen Wert als "0,9" subtrahiert wird, wird das obige Ergebnis erhalten. Im Buchhaltungssystem kann sogar ein Fehler von 1 Yen zu einer Klage führen, was ein großes Problem darstellt.

Lösung

Verwenden Sie BigDecimal, um das Problem zu lösen!

Code

	BigDecimal bigDecimalValue1 = new BigDecimal("1.0");
	BigDecimal bigDecimalValue2 = new BigDecimal("0.9");
	BigDecimal bigDecimalResultValue = bigDecimalValue1.subtract(bigDecimalValue2);

	System.out.println("bigDecimalValue1: " + bigDecimalValue1);
	System.out.println("bigDecimalValue2: " + bigDecimalValue2);
	System.out.println(bigDecimalResultValue.equals(new BigDecimal("0.1")));
	System.out.println("Subtraktionsergebnis: " + bigDecimalResultValue);

** Ausführungsergebnis **

	bigDecimalValue1: 1.0
	bigDecimalValue2: 0.9
	true
Subtraktionsergebnis: 0.1

BigDecimal enthält den Wert in Form der Verschiebung des Bruchteils zum ganzzahligen Teil. Da alle Ganzzahlen durch endliche Binärzahlen dargestellt werden können, kann BigDecimal verwendet werden, um Gleitkommazahlen genau zu berechnen und zu vergleichen.

Die Methode lautet Siehe offizielle Referenz.

Geschwindigkeit

Da BigDecimal ein Referenztyp ist, ist er natürlich langsamer als grundlegende Datentypoperationen. Lass uns nachsehen.

Code

	double doubleValue1 = 1.0d;
	double doubleValue2 = 0.9d;

	long startDouble = System.currentTimeMillis();

	//100 Millionen Abzüge(double)
	for(int i = 0; i < 100_000_000 ; i++){
		double resultValue = doubleValue1 - doubleValue2;
	}

	long endDouble = System.currentTimeMillis();

	BigDecimal bigDecimalValue1 = new BigDecimal("1.0");
	BigDecimal bigDecimalValue2 = new BigDecimal("0.9");

	long startBigDecimal = System.currentTimeMillis();

	//100 Millionen Abzüge(BigDecimal)
	for(int i = 0; i < 100_000_000 ; i++){
		BigDecimal bigDecimalResultValue = bigDecimalValue1.subtract(bigDecimalValue2);
	}

	long endBigDecimal = System.currentTimeMillis();

	System.out.println("doppeltes Messergebnis: " + (endDouble - startDouble) + "ms");
	System.out.println("bigDecimal Messergebnis: " + (endBigDecimal - startBigDecimal) + "ms");

** Ausführungsergebnis **

doppeltes Messergebnis: 3ms
bigDecimal Messergebnis: 314ms

Es gab einen beträchtlichen Unterschied. Immerhin ist der Basisdatentyp schnell!

Ich persönlich frage mich, ob es kein Problem mit Big Decimal gibt, es sei denn, es gibt viele Schleifen oder es ist Leistung erforderlich.

Wenn du wegläufst, renn weg

Es gibt Zeiten, in denen Sie sich nicht zwingen müssen, Gleitkommazahlen zu verwenden.

Angenommen, Sie haben ein System, das Ihr Gewicht bis zur ersten Ziffer des Bruchs registriert. Selbst wenn es mit 65,3 kg in Bezug auf die Schnittstelle empfangen wird, kann es normal mit int verarbeitet werden, wenn es im vorgelagerten Teil des Programms um eins verschoben und intern mit 653 behandelt wird. Frei von mühsamer Gleitkomma-Verarbeitung!

Dies wirkt sich jedoch auch auf die Spaltendefinition der Datenbank aus. Daher ist es gut, in der Entwurfsphase eine feste Entscheidung zu treffen.

Recommended Posts

Konfrontieren Sie Java-Gleitkommafehler
Der heutige Java-Fehler
Gegenmaßnahmen gegen Java-Fehler
Fehlerquellen bei Java Math.sqrt
Vermeiden Sie den Fehler, den Yuma in Java gemacht hat
Was ist ein Gleitkomma?
Fehlerbehebung von Java Setter Getter
Fehler beim Spielen mit Java
Zusammenfassung der Java-Fehlerverarbeitung
Umgang mit Java-Gleitkomma Beim Lesen des Nachschlagewerks [Hinweis]
[Hinweis] Behandlung von Java-Dezimalstellen