# Rundungsfehler?



## Chris2103 (6. Apr 2007)

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:

```
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


----------



## 23123234 (6. Apr 2007)

http://de.wikipedia.org/wiki/Gleitkommazahl#Dezimalzahlen


----------



## Wildcard (6. Apr 2007)

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?


----------



## Wildcard (6. Apr 2007)

Hier übrigens die Erklärung:
http://java.sun.com/javase/6/docs/api/java/lang/Double.html#toString(double)
Java ist schon clever  :shock:


----------



## Guest (6. Apr 2007)

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:


```
double d = 0.2*0.2-0.04;
```

das selbe Ergebnis bekomme, also:

double: 0.00000000000000000694


----------



## Wildcard (7. Apr 2007)

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 (7. Apr 2007)

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 (7. Apr 2007)

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 (7. Apr 2007)

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   
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 (8. Apr 2007)

"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.


----------

