Warum liefert diese Fließkommaberechnung auf verschiedenen Maschinen unterschiedliche Ergebnisse?

8

Ich habe eine einfache Routine, die das Seitenverhältnis aus einem Fließkommawert berechnet. Für den Wert 1.77777779 gibt die Routine die Zeichenfolge "16: 9" zurück. Ich habe das auf meiner Maschine getestet und es funktioniert gut.

Die Routine wird wie folgt angegeben:

%Vor%

Jetzt auf einer anderen Maschine bekomme ich völlig andere Ergebnisse. Also auf meiner Maschine gibt 1.77777779 "16: 9", aber auf einer anderen Maschine bekomme ich "38:21".

    
JD. 26.02.2010, 14:50
quelle

4 Antworten

24

Hier ist ein interessantes Teil der C # -Spezifikation aus Abschnitt 4.1.6:

  

Gleitkommaoperationen können sein   mit höherer Präzision ausgeführt als   der Ergebnistyp der Operation Zum   B. einige Hardware-Architekturen   Unterstütze ein "erweitertes" oder "langes Doppel"   Fließkommatyp mit größerer Reichweite   und Präzision als der doppelte Typ,   und implizit alle durchführen   Fließkommaoperationen, die dies verwenden   höherer Präzisionstyp. Nur bei   Übermäßige Kosten in der Leistung können z   Hardware-Architekturen gemacht werden   Gleitkommaoperationen mit durchführen   weniger Präzision, und nicht   erfordern eine Implementierung zu verfallen   sowohl Leistung als auch Präzision, C #   erlaubt eine höhere Präzision zu sein   Wird für alle Fließkommazahlen verwendet   Operationen. Anders als mehr zu liefern   präzise Ergebnisse, das hat selten was   messbare Effekte.

Es ist möglich, dass dies einer der "messbaren Effekte" ist, dank diesem Aufruf an Ceiling. Nimmt man die Obergrenze einer Gleitkommazahl, wie andere angemerkt haben, vergrößert sich eine Differenz von 0,000000002 um neun Größenordnungen, weil aus 15,99999999 in 16 und 16,00000001 in 17 wird. Zwei Zahlen, die vor der Operation geringfügig abweichen, unterscheiden sich danach massiv; Der kleine Unterschied könnte auf die Tatsache zurückzuführen sein, dass verschiedene Maschinen mehr oder weniger "Extra-Präzision" in ihren Fließkommaoperationen haben können.

Einige verwandte Probleme:

Um Ihr spezifisches Problem zu lösen, wie man ein Seitenverhältnis aus einem Float berechnen kann: Ich würde das möglicherweise auf eine völlig andere Art und Weise lösen. Ich würde einen Tisch so machen:

%Vor%

und jetzt ist Ihre Implementierung

%Vor%

Beachten Sie, dass sich der Code nun ähnlich wie die Operation verhält, die Sie ausführen möchten: Wählen Sie aus dieser Liste mit den üblichen Ratios diejenige aus, bei der die Differenz zwischen dem angegebenen Verhältnis und dem gemeinsamen Verhältnis minimiert ist.

    
Eric Lippert 26.02.2010, 17:04
quelle
7

Ich würde keine Fließkommazahlen verwenden, wenn ich nicht wirklich müsste. Sie sind aufgrund von Rundungsfehlern zu anfällig für solche Dinge.

Können Sie den Code in doppelter Genauigkeit ändern? (Dezimal wäre Overkill). Wenn Sie dies tun, gibt es konsistentere Ergebnisse?

Warum unterscheiden sich die verschiedenen Maschinen voneinander? Was sind die Unterschiede zwischen den beiden Maschinen?

  • 32 bit vs 64 bit?
  • Windows 7 vs Vista vs XP?
  • Intel vs AMD-Prozessor? (Danke Oded)

So etwas wie könnte die Ursache sein.

    
ChrisF 26.02.2010 14:54
quelle
5

Versuchen Sie Math.Round anstelle von Math.Ceiling . Wenn Sie mit 16.0000001 enden und aufrunden, werden Sie diese Antwort falsch verwerfen.

Verschiedene andere Vorschläge:

  • Doppelte sind besser als schweben.
  • (double) 0.1 cast ist unnötig.
  • Möchte eine Ausnahme auslösen, wenn Sie nicht wissen, wie das Seitenverhältnis ist.
  • Wenn du sofort zurückkommst, wenn du die Antwort gefunden hast, kannst du die Variable carryon ablegen.
  • Eine vielleicht genauere Überprüfung wäre, das Seitenverhältnis für jede Schätzung zu berechnen und sie mit der Eingabe zu vergleichen.

Überarbeitet (ungetestet):

%Vor%     
John Kugelman 26.02.2010 14:56
quelle
2

Wenn der Index 9 ist, erwarten Sie etwas wie upper = 16.0000001 oder upper = 15.9999999. Welche Sie erhalten, hängt vom Rundungsfehler ab, der auf verschiedenen Maschinen unterschiedlich sein kann. Wenn es 15.999999 ist, ist roundedUpValue - upper <= 0.1 wahr und die Schleife endet. Wenn es 16.0000001 ist, ist roundedUpValue - upper <= 0.1 falsch und die Schleife läuft weiter, bis Sie index > 20 erreichen.

Stattdessen sollten Sie vielleicht versuchen, auf die nächste ganze Zahl zu runden und zu überprüfen, ob der absolute Wert der Differenz von dieser ganzen Zahl klein ist. In anderen Worten, verwenden Sie etwas wie if (Math.Abs(Math.Round(upper) - upper) <= (double)0.0001 || index > 20)

    
Tim Goodman 26.02.2010 14:58
quelle

Tags und Links