Rundungsfehler?

Status
Nicht offen für weitere Antworten.
C

Chris2103

Gast
Hallo,

ich habe folgendes Problem: Wenn ich die Zahl 0.1 in einem double speichere, wird diese Zahl im IEEE 754 Format gespreichert, d.h. es wird gerundet. Wenn ich die Zahl ausgebe erwarte ich also ein Zahl != 0.1.

Mit dem Code:
Code:
public class Main
{
	public static void main(String[] args)
	{
		double d = 0.1;
		System.out.printf("double: %.20f%n", d);
	}
}
wird allerdings folgendes ausgegeben:

double: 0,10000000000000000000

Zuerst dachte ich, dass sich 0.1 vielleicht zufälligerweise genau darstellen lässt, aber das selbe Programm in C++ ergibt dann folgende Ausgabe:

double: 0.10000000000000000555

Da bei Java und C++ der double Type angeblich auf dem IEEE 754 Standard basiert (auch beide 64 Bit), ist nun die Frage, wo der Fehler liegt??? Rundet printf obwohl ich 20 Nachkommastellen Genauigkeit wollte? Kann ich den "echten" double Wert in Java irgendwie ausgeben?

Gruß, Chris
 

Wildcard

Top Contributor
Chris2103 hat gesagt.:
Da bei Java und C++ der double Type angeblich auf dem IEEE 754 Standard basiert (auch beide 64 Bit)
Ohne das strictfp Keyword verwendet Java nicht die IEEE Darstellung sondern die öft etwas größere Genauigkeit der CPU.
Sind also vermutlich mehr als 64 Bit.
Hast du mal ausgerechnet was tatsächlich bei angenommenen 64 bit passieren rauskommen müsste?
 
G

Guest

Gast
Danke für den Tipp. Ich habe jetzt folgendes rausgefunden. Wenn ich den MIN_VALUE ausgeben, dann kommt folgendes raus:

Min Value: 0,00000000000000000000

Das ist natürlich Quatsch, darum habe ich die Nachkommastellen erhöht, und siehe da... :)

Min Value: 0,00000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000049

Also ist die Genauigkeit größer als ich erwartet habe (IEEE 754 sagt was von 15 Stellen Genauigkeit, genau muss ich erst noch nachsehen).

Komisch ist nur dass ich in Java, genauso wie ich C++ für folgendes:

Code:
  double d = 0.2*0.2-0.04;

das selbe Ergebnis bekomme, also:

double: 0.00000000000000000694
 

Wildcard

Top Contributor
Du scheinst ein grundsätzliches Verständnisproblem mit Gleitkommazahlen wenn dich überrascht das hier auf mehr als 15 Stellen genau gerechnet werden kann. ???:L
 

Marco13

Top Contributor
Vielleicht sollte man es nochmal erwähnen. Man kann mit double GANZ GENAU mit Zahlen rechnen wie (zum Beispiel!)
0.00000000000000000000000000000000000000123

Allerdings wird
1 + 0.00000000000000000000000000000000000000123
dann nur
1
ergeben, und nicht
1.00000000000000000000000000000000000000123

Es geht um die Anzahl der signifikanten Stellen, und die ist tatsächlich auf ca. 15 beschränkt. Und GANZ GENAU rechnet man auch nur, wenn die entsprechende Zahl "zufällig" genau darstellbar ist (was für 0.1 schonmal nicht gilt). Man wird sich also fast immer mit Rundungsfehlern abfinden müssen, weil man nur ca. 15 Stellen zur Verfügung hat (die aber für sehr große oder sehr kleine Zahlen verwendet werden können)
 

kleiner_held

Top Contributor
Wenn man mit einer fest definierten Genauigkeit bei den Nachkommastellen rechnen will (z.B.: bei Finanzanwendungen), sollte man auf java.math.BigDecimal ausweichen, da Gleitkommazahlen auch die häßliche Angewohnheit haben, mit zunehmender Größe der Werte vor dem Komma an Genauigkeit hinter dem Komma zu verlieren.
 

Marco13

Top Contributor
kleiner_held hat gesagt.:
Wenn man mit einer fest definierten Genauigkeit bei den Nachkommastellen rechnen will (z.B.: bei Finanzanwendungen), sollte man auf java.math.BigDecimal ausweichen, da Gleitkommazahlen auch die häßliche Angewohnheit haben, mit zunehmender Größe der Werte vor dem Komma an Genauigkeit hinter dem Komma zu verlieren.

Ja, das hab ich neulich auch gemerkt, als ich ein Verwaltungsprogramm für meinen Kontostand schreiben wollte :cool:
Mal im ernst: Da wird wohl BigDecimal kaum die geeignete Lösung sein. Da kann man nämlich auch "nur" sagen, wo hin gerundet werden soll, aber "unendlich genau" ist das auch nicht (und außerdem grottenlahm). Im kaufmännischen Bereich (bzw. Banken) werden AFAIK eher BCD Zahlen http://de.wikipedia.org/wiki/BCD-Code verwendet.
 

kleiner_held

Top Contributor
"unendlich genau" kriegt man sowieso nicht hin, 1 / 3 lässt sich ohne runden nun mal nicht abbilden, außer man speichert jede Zahl als Bruch aus Nenner und Zähler. BCD kannte ich bisher noch nicht, scheint mir aber auf den ersten Blick jetzt keinen Genauigkeitsunterschied zu BigDecimal zu bieten, benötigt aber mehr Speicherplatz.
J2EE Anwendungen aus dem Finanzbereich arbeiten tatsächlich mit BigDecimal - man rundet auf irgendwas um die 8 - 10 Stellen hinter dem Komma (ist glaub ich irgendwo in Basel II festgelegt), die Geschwindigkeit ist erträglich, Genauigkeit (im Vergleich zu double) geht halt vor. Durch MathContext in 1.5 ist Rechnen mit BigDecimal und festgelegter Nachkommastellen-Anzahl eigentlich sehr komfortabel.
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben