# Rundungsfehler



## fatfox (21. Jul 2011)

Hi alle,

kann jemand mir mal erklären,

```
System.out.println(1.0-0.9);
```

Das Output ist 0.09999999999999998 und nicht 0.1, warum???? Kann jemand mir die Theorie erklären? (Das output in C++ ist 0.1, ganz in Ordnung.)


----------



## XHelp (21. Jul 2011)

Das hat nichts mit Java zu tun, sondern mit der Binärdarstellung der Gleitkommazahlen. Darüber gibt es gefühlte 999999999 Themen hier im Forum und Seiten im Internet, da wirst du bestimmt weitere Infromationen finden.


----------



## Final_Striker (21. Jul 2011)

fatfox hat gesagt.:


> Kann jemand mir die Theorie erklären?



Da hast du deine Theorie: Gleitkommazahl


----------



## fatfox (21. Jul 2011)

XHelp hat gesagt.:


> Das hat nichts mit Java zu tun, sondern mit der Binärdarstellung der Gleitkommazahlen. Darüber gibt es gefühlte 999999999 Themen hier im Forum und Seiten im Internet, da wirst du bestimmt weitere Infromationen finden.



Hi XHelp, könntest du mir einen Keyword vorschlagen, damit ich sie in Google suchen kann? Im Forum habe ich eben leider keine Ähnliche Themen gefunden...


----------



## fatfox (21. Jul 2011)

Final_Striker hat gesagt.:


> Da hast du deine Theorie: Gleitkommazahl



Danke!!! Ich denke, das ist gerade was ich brache!


----------



## RySa (22. Jul 2011)

Theorie :

1/3 = 0.3333333333333
2/3 = 0.6666666666666

dann muss doch

3/3 = 0.9999999999999 

sein, obwohl wir alle wissen, dass 3/3 = 1 ist.

Da haste deine Verschwörungstheorie. 0.999999999 gibt es nicht :autsch:

Btw. Thema als erledigt markieren wäre auch nicht schlecht...


----------



## glga (22. Jul 2011)

RySa hat gesagt.:


> dann muss doch
> 
> 3/3 = 0.9999999999999
> 
> sein, obwohl wir alle wissen, dass 3/3 = 1 ist.


0.9999999999999... = 1


----------



## RySa (22. Jul 2011)

glga hat gesagt.:


> 0.9999999999999... = 1



Darauf wollte ich auch hinaus...Sarkasmus ist anscheinend nicht allen bekannt..


----------



## glga (22. Jul 2011)

habe jetzt auch nach fünften mal durchlesen keine ironie entdecken können. da du dich anscheinend schlecht ausdrücken kannst, mach doch das nächste mal einen zwinkersmiley. ich verstehe den zusammenhang von deinem posting zum thread eigentlich auch überhaupt nicht. sieht mir irgendwie nach blöden kommentar ablassen auf kosten anderer aus.


----------



## fatfox (24. Jul 2011)

RySa hat gesagt.:


> Theorie :
> 
> 1/3 = 0.3333333333333
> 2/3 = 0.6666666666666
> ...



Hi RySa,

1. ich finde, das ist keine richtige Erklärung. Ich möchte gern wissen, *wie java die "1.0-0.9" implementiert.  C++ können "0.1" liefern, aber warum java nicht?! *

Obwohl die Differenz zwischen 0.1 und 0.09999999999999998 sehr klein ist, aber wenn man über 10000 mal summieren oder substrahieren, dann wird einen großen Fehler erzeugen. 

2. Man kann auch folgende code probieren:


```
public class Test {
    public static void main(String[] args) {
        float a = 2.8788f;
        float b = 0.711f;
        float c = 2.5286f;
 
        System.out.println(a + b + c);
        System.out.println(a + c + b);
    }
}
```

java liefert zwei verschiedenen Ergebnis, obwoahl float in java hat 7 stellige Genauigkeit. C++ liefert immer das richtige Ergebnis. (Das Beispiel hat SlaterB mir gegeben: http://www.java-forum.org/java-basics-anfaenger-themen/111902-arrays-sort-problem.html#_)

Ich habe die Sache noch nicht ganz verstanden, deshalb würde ich das Thema nicht als "erledigt" markieren.


----------



## Ark (24. Jul 2011)

Mit strictfp wird in Java in jedem Fall immer nach dem gleichen Verfahren gerechnet, und zwar dank einer Bibliothek namens fdlibm (siehe Erklärungen zu java.lang.StrictMath).

Nun ist das so: Konstanten im Quelltext werden in Java in bestimmten Umständen (z.B. in diesem Fall) gefaltet, d.h., die Ergebnisse der Additionen im genannten Beispiel werden bereits fertig berechnet in den Bytecode gegossen. Zur Faltung verwendet der Java-Compiler ebenfalls immer die oben genannte Bibliothek. Damit wird sichergestellt, dass der gleiche Quelltext immer das gleiche Kompilat liefert, unabhängig davon, auf welcher Maschine kompiliert wird.

Bei C/C++ kann es jedoch sein, dass der dort verwendete Compiler zur Konstantenfaltung einfach den Code zur Berechnung auf der Maschine ausführt, auf der er kompiliert wird, und anschließend das Ergebnis in das Kompilat einsetzt. Damit wird das Ergebnis beim Kompilieren also abhängig davon, welche Maschine dabei benutzt wird.

Es kann also sein, dass bei C/C++ der Compiler mit einer viel höheren Genauigkeit (je nach Hardware etc., z.B. mit double-Genauigkeit) gerechnet hat und sich dadurch das Rundungsproblem an der Stelle nicht bemerkbar macht. Der Java-Compiler rechnet hier aber strikt mit float-Genauigkeit, und dadurch bleibt ein deutlich sichtbarer Fehler übrig.

Ark


----------



## HoaX (24. Jul 2011)

Edit: Ich sollte öfter F5 drücken und Tabs nich zu lange offen lassen...


----------



## RySa (25. Jul 2011)

> java liefert zwei verschiedenen Ergebnis, obwoahl float in java hat 7 stellige Genauigkeit.



Nur weil der float 7 (manchmal auch 8) Stellen speichern und anzeigen kann, heißt es nicht dass sie "genau" sind, da er ggf. runden muss.
Warum das so ist, dass der double bzw. float "falsch" rechnet, hat der _Ark_ glaube ich deutlich erklärt (ich selbst habe ehrlich gesagt keine Ahnung, was da intern genau passiert). Was ich aber weiß ist, dass doubles bzw floats nicht für präzise Berechnungen geignet sind, weil sie einfach nicht dafür "gemacht" worden sind. Dafür gibt es unter anderen das BigDecimal. Also wenn du Präzision brauchst, benutze diese Klasse dafür.


----------



## tuttle64 (25. Jul 2011)

Lieber fatfox,

zuerst einmal zum Einbrennen (Effective Java Second Edition, J. Bloch): 



> *Avoid float and double if exact answers are required!*




Zweitens, lese bitte im folgenden Link den Erläuterungen zu Punkt 20: Java Beginners Faq at JavaRanch

Verfolge und studiere bitte die zwei in Punkt 20 aufgeführten Links.


----------



## fatfox (25. Jul 2011)

tuttle64 hat gesagt.:


> Lieber fatfox,
> 
> zuerst einmal zum Einbrennen (Effective Java Second Edition, J. Bloch):
> 
> ...



Liebe tuttle647,

Vielen Dank! Ich denke, die "Floating-Point Arithmetic" ist richtige der Grund, wie obigen Probleme vorgekommen sind.  Floating-point kann die 0.1 nicht richtig darstellen. Aber welche Algorithmus hat C++ benutzt? Wieso kann C++ immer die richtige Lösung liefern? Bedeutet C++ genauer als Java ist?


----------



## Weltatlas (25. Jul 2011)

Genau das mit C++ hat Ark doch erklärt…


----------



## RySa (25. Jul 2011)

fatfox hat gesagt.:
			
		

> Wieso kann C++ immer die richtige Lösung liefern? Bedeutet C++ genauer als Java ist?



Wurde schon erklärt...


Ark hat gesagt.:


> Bei C/C++ kann es jedoch sein, dass der dort verwendete Compiler zur Konstantenfaltung einfach den Code zur Berechnung auf der Maschine ausführt, auf der er kompiliert wird, und anschließend das Ergebnis in das Kompilat einsetzt. Damit wird das Ergebnis beim Kompilieren also abhängig davon, welche Maschine dabei benutzt wird.
> 
> Es kann also sein, dass bei C/C++ der Compiler mit einer viel höheren Genauigkeit (je nach Hardware etc., z.B. mit double-Genauigkeit) gerechnet hat und sich dadurch das Rundungsproblem an der Stelle nicht bemerkbar macht.



Wenn du schon Fragen stellst, dann ließ bitte zumindest alle Antworten...


----------



## Ark (26. Jul 2011)

Möglicherweise wurde ich sprachlich nicht verstanden, zumindest gehe ich einfach mal davon aus. Deshalb noch einmal ein Testfall:

```
private static strictfp void test(){
		final float fa = Float.parseFloat("1.0");
		final float fb = Float.parseFloat("0.9");
		final float fres = fa - fb;
		System.out.println("float: " + fres);

		final double da = Double.parseDouble("1.0");
		final double db = Double.parseDouble("0.9");
		final double dres = da - db;
		System.out.println("double: " + dres);

		final BigDecimal ba = new BigDecimal("1.0");
		final BigDecimal bb = new BigDecimal("0.9");
		final BigDecimal bres = ba.subtract(bb);
		System.out.println("BigDecimal: " + bres);
		System.out.println("BigDecimal als double: " + bres.doubleValue());
	}
```
Ausgabe:

```
float: 0.100000024
double: 0.09999999999999998
BigDecimal: 0.1
BigDecimal als double: 0.1
```
double-Genauigkeit scheint für die Berechnung also nicht zu reichen (wie ich für mein Beispiel weiter oben vermutet habe), aber dennoch scheint meine These zu stimmen: *Der C++-Compiler hat mit viel größerer Genauigkeit gerechnet, als für Java spezifiziert ist.* Diese größere Genauigkeit habe ich hier mit BigDecimal simuliert. (Beim C++-Compiler können das z.B. 80-Bit-Gleitkommazahlen gewesen sein, aber auch hier möchte ich mich nicht festlegen.)

Ark


----------



## Der Müde Joe (26. Jul 2011)

Noch ein bissel mehr Therie:
What Every Computer Scientist Should Know About Floating-Point Arithmetic


----------

