Warum 0,1 + 0,2 == 0,3 in D?

75
assert(0.1 + 0.2 != 0.3); // shall be true

ist meine Lieblingsprüfung, dass eine Sprache native Gleitkomma-Arithmetik verwendet.

C ++

#include <cstdio>

int main()
{
   printf("%d\n", (0.1 + 0.2 != 0.3));
   return 0;
}

Ausgabe:

1

http://ideone.com/ErBMd

Python

print(0.1 + 0.2 != 0.3)

Ausgabe:

True

http://ideone.com/TuKsd

Andere Beispiele

Warum gilt das nicht für D? Nach dem Verständnis verwendet D native Gleitkommazahlen. Ist das ein Fehler? Verwenden sie eine bestimmte Zahlendarstellung? Etwas anderes? Ziemlich verwirrend.

D.

import std.stdio;

void main()
{
   writeln(0.1 + 0.2 != 0.3);
}

Ausgabe:

false

http://ideone.com/mX6zF


AKTUALISIEREN

Danke an LukeH . Dies ist ein Effekt von Gleitkommakonstante Folding beschrieben dort .

Code:

import std.stdio;

void main()
{
   writeln(0.1 + 0.2 != 0.3); // constant folding is done in real precision

   auto a = 0.1;
   auto b = 0.2;
   writeln(a + b != 0.3);     // standard calculation in double precision
}

Ausgabe:

false
true

http://ideone.com/z6ZLk

Stas
quelle
13
Bitte setzen Sie relevante Codebeispiele direkt in die Frage und nicht auf externe Links. Beides, um sicherzustellen, dass die vollständigen Informationen in der Frage erhalten bleiben, und um das Lesen zu erleichtern.
Anders Abel
6
Ich wollte reflexartig auf die Schaltfläche zum Schließen klicken, bis ich bemerkte, dass Sie ==statt geschrieben haben !=.
dan04
2
In Bezug auf Ihr Update: Dies ist kein "Problem" mit dem Compiler-Optimierer. Es handelt sich um ein legales Gleitkomma-Verhalten, und die Möglichkeit, dass dies geschieht, wird im Abschnitt "Gleitkommakonstante Faltung" der D-Dokumentation erläutert .
LukeH
1
Bitte schauen Sie sich an, was passiert, wenn Sie den realTyp anstelle des doubleTyps verwenden: ideone.com/NAXkM
Jean Hominal
@ Jean Hominal: Fall mit echtem Typ ist interessant. Denken ...
Stas

Antworten:

47

Es wird wahrscheinlich auf (0,3! = 0,3) optimiert. Welches ist offensichtlich falsch. Überprüfen Sie die Optimierungseinstellungen, stellen Sie sicher, dass sie ausgeschaltet sind, und versuchen Sie es erneut.

Flynn1179
quelle
27
Warten Sie, warum führt der Compiler eine Dezimal-Gleitkomma-Berechnung und die Laufzeit eine binäre Gleitkomma-Berechnung durch?
Jean Hominal
Guter Punkt. Das Lustige ist, ich habe es gerade versucht und ich werde falsch; Ich kann das Ergebnis des OP nicht selbst reproduzieren. Ich kompiliere auf 32 Bit und frage mich, ob 64 Bit einen Unterschied macht.
Flynn1179
13
Dies ist die richtige Antwort. Siehe den Abschnitt "Gleitkommakonstante Faltung" von d-programming-language.org/float.html .
LukeH
1
Auf jeden Fall etwas mit Optimierung. Versuchte das gleiche mit Variablen und wurde wahr: ideone.com/zO4OD
bezmax
3
Heh, ich habe die Frage gerade noch einmal gelesen. Ich dachte mit 'D', Sie meinten das vierte Beispiel in dieser Liste; Ich habe versucht, es in C # zu wiederholen!
Flynn1179
53

(Flynns Antwort ist die richtige Antwort. Diese Antwort befasst sich allgemeiner mit dem Problem.)


Sie scheinen davon auszugehen, OP, dass die Gleitkomma-Ungenauigkeit in Ihrem Code deterministisch und vorhersehbar falsch ist (in gewisser Weise ist Ihr Ansatz das genaue Gegenteil von dem von Menschen, die Gleitkomma noch nicht verstehen).

Obwohl (wie Ben weist darauf hin) Gleitkommazahlen Ungenauigkeit ist deterministisch , aus der Sicht des Codes, wenn Sie nicht sehr werden über überlegen , was bei jedem Schritt auf Ihre Werte geschieht, wird dies nicht der Fall sein. Eine beliebige Anzahl von Faktoren kann zu einem 0.1 + 0.2 == 0.3Erfolg führen, wobei die Optimierung zur Kompilierungszeit einer ist und die optimierten Werte für diese Literale ein anderer sind.

Verlassen Sie sich hier weder auf Erfolg noch auf Misserfolg; Verlassen Sie sich in keiner Weise auf die Gleitkomma-Gleichheit .

Leichtigkeitsrennen im Orbit
quelle
25
Das ist ein sehr guter Punkt - Sie können sich nicht auf Gleitkomma-Arithmetik verlassen, um die falsche Antwort zu erhalten! :-)
Steve Morgan
8
Gleitkomma-Ungenauigkeit liefert deterministische, vorhersehbare Antworten ... solange Sie Sequenzpunkte und Zuweisungen zu Variablen verwenden, um bei jedem Schritt eine Rundung zu erzwingen. Achten Sie auch auf Compiler-Optionen, die Rundungen vermeiden, z. B. bei MSVC /fp:precise.
Ben Voigt
7
Dies ist eine schreckliche Erklärung. IEEE 754 definiert eindeutig grundlegende Operationen einschließlich +. Das Problem hierbei ist die Programmiersprache, nicht das Gleitkomma. Auch die Gleitkomma-Gleichheit ist perfekt definiert. Sie sollten es nicht verwenden, wenn es nicht das ist, was Sie wollen, das ist alles.
Pascal Cuoq
@Pascal: IEEE 754 tut es. D nicht. Sie behaupten, dass "das Problem hier eine Programmiersprache ist", und ... Sie haben Recht! Wenn man sich die Frage aussehen wirklich eng, werden Sie sehen , dass es markiert ist d, nicht IEEE 754. Ich hoffe wirklich, dass Ihnen das hilft, die Frage zu verstehen.
Leichtigkeitsrennen im Orbit
@ Ben: Sicher, wenn Sie all diese Faktoren kontrollieren. Meine Antwort setzt voraus, dass der Programmierer das nicht tut. Ich habe meine Antwort auf dieses Wort besser bearbeitet.
Leichtigkeitsrennen im Orbit
5

Nach meiner Interpretation der D-Sprachspezifikation würde die Gleitkomma-Arithmetik auf x86 intern 80 Bit Genauigkeit anstelle von nur 64 Bit verwenden.

Man müsste jedoch überprüfen, ob dies ausreicht, um das beobachtete Ergebnis zu erklären.

Jean Hominal
quelle
6
0,1 ist unendlich.
Leichtigkeitsrennen im Orbit
2
Woah, @Tomalak, mein Kopf ist gerade explodiert ;-)
Steve Morgan
2
@Tomalak: wie 0,2 und 0,3 - aber das Runden mit 80-Bit-Genauigkeit anstelle von 64 könnte den Wert "gleich" anstatt eindeutig machen. Und ich habe gerade mit Variablen mit dem realen Typ überprüft, und es wird wieder als falsch ausgewertet: ideone.com/sIFgk
Jean Hominal