Wie bewertet ein C # den Gleitkommawert im Hover-Over- und Intermediate-Fenster im Vergleich zum Kompilierten?

7

Ich habe etwas Seltsames beim Speichern von Doubles in einem Wörterbuch und bin verwirrt darüber, warum.

Hier ist der Code:

%Vor%

Die zweite if-Anweisung funktioniert wie erwartet; 1,0 ist nicht weniger als 1,0. Jetzt wird die erste if-Anweisung als wahr ausgewertet. Die seltsame Sache ist, dass, wenn ich den Mauszeiger über das if halte, das IntelliSense-Element "False" sagt, der Code jedoch glücklich in Console.WriteLine verschoben wird.

Dies ist für C # 3.5 in Visual Studio 2008.

Ist das ein Gleitkomma-Genauigkeitsproblem? Warum funktioniert dann die zweite if-Anweisung? Ich fühle, dass mir hier etwas sehr Grundlegendes fehlt.

Jede Einsicht ist willkommen.

Edit2 (Frage ein wenig nachstellen):

Ich kann das Mathepräzisionsproblem akzeptieren, aber meine Frage ist jetzt: Warum wird der Hover über richtig ausgewertet? Dies gilt auch für das Zwischenfenster. Ich füge den Code aus der ersten if-Anweisung in das Zwischenfenster ein und es wird false ausgewertet.

Aktualisieren

Zunächst einmal vielen Dank für all die tollen Antworten.

Ich habe auch Probleme, dies in einem anderen Projekt auf demselben Rechner wiederherzustellen. Betrachtet man die Projekteinstellungen, sehe ich keine Unterschiede. Betrachtet man die IL zwischen den Projekten, sehe ich keine Unterschiede. Betrachtet man die Demontage, sehe ich keine offensichtlichen Unterschiede (neben Speicheradressen). Aber wenn ich das ursprüngliche Projekt debugge, sehe ich: Screenshot des Problems http://i30.tinypic.com/ega874.png

Das unmittelbare Fenster sagt mir, ob das falsch ist, aber der Code fällt in die Bedingung.

Die beste Antwort, denke ich, ist jedenfalls, sich in diesen Situationen auf Fließkommaarithmetik vorzubereiten. Der Grund, warum ich das nicht loslassen konnte, hat mehr damit zu tun, dass die Berechnungen des Debuggers von der Laufzeit abweichen. Also vielen Dank Brian Gideon und stephenhentyrone für einige sehr aufschlussreiche Kommentare.

    
Richard Morgan 10.09.2009, 14:04
quelle

4 Antworten

13

Es ist ein Gleitkomma-Problem.

Die zweite Anweisung funktioniert, weil der Compiler den Ausdruck 1e-3 * 1e3 vor der Ausgabe der .exe zählt.

Schaut es in ILDasm / Reflector an, es wird etwas wie

ausgeben %Vor%     
nothrow 10.09.2009, 14:07
quelle
4

Das Problem ist hier ziemlich subtil. Der C # -Compiler gibt (immer) keinen Code aus, der die Berechnung doppelt durchführt, selbst wenn dies der von Ihnen angegebene Typ ist. Insbesondere gibt es Code aus, der die Berechnung in "erweiterter" Genauigkeit unter Verwendung von x87-Anweisungen durchführt, ohne die Zwischenergebnisse zu verdoppeln.

Abhängig davon, ob 1e-3 als Double oder Long Double ausgewertet wird und ob die Multiplikation in Double oder Long Double berechnet wird, ist es möglich, eines der folgenden drei Ergebnisse zu erhalten:

  • (long double) 1e-3 * 1e3 berechnet in long double ist 1.0 - epsilon
  • (doppelt) 1e-3 * 1e3 berechnet im Doppel ist genau 1,0
  • (double) 1e-3 * 1e3 berechnet im langen Doppel ist 1.0 + epsilon

Offensichtlich wird der erste Vergleich, der Ihre Erwartungen nicht erfüllt, auf die im dritten Szenario beschriebene Weise bewertet. 1e-3 wird abgerundet, um entweder zu verdoppeln, weil Sie es speichern und erneut laden, was die Rundung erzwingt, oder weil C # 1e-3 als doppelt präzises Literal erkennt und es so behandelt. Die Multiplikation wird in long double ausgewertet, da C # ein hirntotes Numerikmodell hat, so wie der Compiler den Code generiert.

Die Multiplikation im zweiten Vergleich wird entweder mit einer der beiden anderen Methoden ausgewertet (Sie können herausfinden, was Sie mit "1 & gt; 1e-3 * 1e3" versuchen), oder der Compiler rundet das Ergebnis der Multiplikation vor dem Vergleich mit 1.0, wenn es den Ausdruck zum Zeitpunkt der Kompilierung auswertet.

Es ist wahrscheinlich möglich, dass Sie dem Compiler sagen, keine erweiterte Genauigkeit zu verwenden, ohne dass Sie ihm über eine Build-Einstellung sagen; Codegen zu SSE2 aktivieren könnte auch funktionieren.

    
Stephen Canon 10.09.2009 16:21
quelle
2

Siehe die Antworten hier

    
AnthonyWJones 10.09.2009 14:09
quelle
2

Ähm ... seltsam. Ich kann Ihr Problem nicht reproduzieren. Ich benutze auch C # 3.5 und Visual Studio 2008. Ich habe Ihr Beispiel genau eingegeben , wie es gepostet wurde, und ich sehe weder Console.WriteLine Anweisung ausführen.

Auch die zweite if-Anweisung wird vom Compiler optimiert. Wenn ich sowohl die Debug- als auch die Release-Builds in ILDASM / Reflector untersuche, sehe ich keinen Beweis dafür. Das macht da, weil ich eine Compiler-Warnung bekomme, die besagt, dass unerreichbarer Code darauf entdeckt wurde.

Schließlich sehe ich nicht, dass dies ein Problem mit Fließkomma-Genauigkeit sein könnte. Warum würde der C # -Compiler zwei Doubles statisch anders auswerten als die CLR zur Laufzeit? Wenn das wirklich der Fall wäre, könnte man argumentieren, dass der C # -Compiler einen Fehler hat.

Bearbeiten: Nachdem ich ein wenig mehr darüber nachgedacht habe, bin ich noch mehr davon überzeugt, dass dies ein nicht Problem mit Fließkomma-Genauigkeit ist. Sie müssen entweder über einen Fehler im Compiler oder im Debugger gestolpert sein, oder der Code, den Sie gepostet haben, ist nicht exakt repräsentativ für Ihren tatsächlichen laufenden Code. Ich bin sehr skeptisch gegenüber einem Fehler im Compiler, aber ein Fehler im Debugger scheint wahrscheinlicher. Versuchen Sie, das Projekt neu zu erstellen und es erneut auszuführen. Vielleicht sind die Debugging-Informationen, die mit der Exe kompiliert wurden, nicht mehr synchron oder so.

    
Brian Gideon 10.09.2009 14:42
quelle

Tags und Links