Rundungsfehler

fatfox

Bekanntes Mitglied
Hi alle,

kann jemand mir mal erklären,
Java:
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

Top Contributor
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.
 

fatfox

Bekanntes Mitglied
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...
 

RySa

Bekanntes Mitglied
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 :eek::autsch:

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

glga

Gast
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

Bekanntes Mitglied
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 :eek::autsch:

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

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:

Java:
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.
 
Zuletzt bearbeitet:

Ark

Top Contributor
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
 

RySa

Bekanntes Mitglied
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.
 

fatfox

Bekanntes Mitglied
Lieber fatfox,

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




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.

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?
 

RySa

Bekanntes Mitglied
fatfox hat gesagt.:
Wieso kann C++ immer die richtige Lösung liefern? Bedeutet C++ genauer als Java ist?

Wurde schon erklärt...
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

Top Contributor
Möglicherweise wurde ich sprachlich nicht verstanden, zumindest gehe ich einfach mal davon aus. Deshalb noch einmal ein Testfall:
Java:
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:
Code:
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
 

Ähnliche Java Themen

Neue Themen


Oben