Warum wirkt sich die Reihenfolge auf die Rundung aus, wenn mehrere Doppel in C # hinzugefügt werden?

7

Betrachten Sie den folgenden C # -Code:

%Vor%

result1 sollte immer gleich result2 gleich sein? Die Sache ist, es tut es nicht. result1 ist 3.3 und result2 ist 3.3000000000000003. Der einzige Unterschied ist die Reihenfolge der Konstanten.

Ich weiß, dass Doubles so implementiert sind, dass Rundungsprobleme auftreten können. Ich bin mir bewusst, dass ich stattdessen Dezimalzahlen verwenden kann, wenn ich absolute Präzision brauche. Oder dass ich Math.Round () in meiner if-Anweisung verwenden kann. Ich bin nur ein Nerd, der verstehen will, was der C # -Compiler tut. Kann mir das jemand sagen?

Bearbeiten:

Vielen Dank an alle, die bisher vorgeschlagen haben, die Gleitkommaarithmetik zu lesen und / oder über die inhärente Ungenauigkeit der Doppelgriffe der CPU zu sprechen. Aber ich glaube, der Hauptstoß meiner Frage ist noch immer unbeantwortet. Was ist meine Schuld, weil ich es nicht richtig formuliert habe? Lass es mich so sagen:

Wenn ich den obigen Code durchbruche, würde ich erwarten, dass die folgenden Operationen stattfinden:

%Vor%

Nehmen wir an, dass jeder der obigen Zusätze einen Rundungsfehler (nummeriert e1..e4) hatte. So enthält r1 den Rundungsfehler e1, r2 enthält Rundungsfehler e1 + e2, r3 enthält e3 und r4 enthält e3 + e4.

Nun, ich weiß nicht, wie genau die Rundungsfehler passieren, aber ich hätte erwartet, dass e1 + e2 gleich e3 + e4 ist. Natürlich nicht, aber das scheint mir irgendwie falsch zu sein. Eine andere Sache ist, dass wenn ich den obigen Code ausführen, bekomme ich keine Rundungsfehler. Das ist es, was mich denken lässt, dass es der C # -Compiler ist, der etwas seltsames statt der CPU macht.

Ich weiß, dass ich viel frage und vielleicht ist die beste Antwort, die jemand geben kann, ein PHD im CPU-Design zu machen, aber ich dachte nur, ich würde fragen.

Bearbeiten 2

Betrachtet man die AWL aus meinem ursprünglichen Codebeispiel, ist klar, dass es der Compiler ist, nicht die CPU, die das tut:

%Vor%

Der Compiler addiert die Zahlen für mich!

    
d4nt 30.03.2009, 11:05
quelle

7 Antworten

10
  

Ich hätte erwartet, dass e1 + e2 gleich e3 + e4 wäre.

Das ist nicht ganz anders als erwartet

%Vor%

entspricht

%Vor%

außer du multiplizierst mit 2 ^ 53 bevor du das Wort ergreifst.

Verwenden von 12-Bit-Gleitkommazahl und Abschneiden mit Ihren Werten:

%Vor%

Wenn Sie also die Reihenfolge der Operationen / Kürzungen ändern, ändert sich der Fehler und r4! = r2. Wenn Sie 1.1 und 1.2 in diesem System hinzufügen, trägt das letzte Bit, also beim Abschneiden nicht verloren. Wenn Sie 1.0 zu 1.1 hinzufügen, ist das letzte Bit von 1.1 verloren und das Ergebnis ist nicht das gleiche.

In einer Reihenfolge entfernt die Rundung (durch Abschneiden) eine abschließende 1 .

In der anderen Reihenfolge, die Rundung entfernt eine abschließende 0 beide Male.

Eins ist nicht gleich Null; also sind die Fehler nicht gleich.

Doppelpunkte haben viel mehr Bits der Genauigkeit, und C # verwendet wahrscheinlich eher Runden als Abschneiden, aber hoffentlich zeigt dieses einfache Modell, dass verschiedene Fehler mit unterschiedlichen Ordnungen der gleichen Werte auftreten können.

Der Unterschied zwischen fp und maths ist, dass + eine Abkürzung für 'add then round' ist und nicht einfach nur add '.

    
Pete Kirkham 30.03.2009, 13:31
quelle
6

Der c # -Compiler macht nichts. Die CPU ist.

Wenn Sie A in einem CPU-Register haben und Sie dann B hinzufügen, ist das in diesem Register gespeicherte Ergebnis A + B, angenähert an die verwendete Gleitkommazahl

Wenn Sie dann C hinzufügen, summiert sich der Fehler. Diese Fehleraddition ist keine transitive Operation, also der letzte Unterschied.

    
Brann 30.03.2009 11:09
quelle
4

Siehe das klassische Papier (Was jeder Informatiker über Gleitkommaarithmetik wissen sollte) zum Thema. Diese Art von Sachen passiert mit Gleitkommaarithmetik. Es braucht ein Informatiker, um Ihnen zu sagen, dass 1/3 + 1/3 + 1/3 is'nt gleich 1 ...

ist     
Pontus Gagge 30.03.2009 11:18
quelle
2

Reihenfolge der Fließkommaoperationen ist wichtig. Antwortet nicht direkt auf Ihre Frage, aber Sie sollten immer vorsichtig sein, Gleitkommazahlen zu vergleichen. Es ist üblich, eine Toleranz einzufügen:

%Vor%

Dies könnte von Interesse sein: Was jeder Informatiker über Gleitkommaarithmetik wissen sollte

    
Mitch Wheat 30.03.2009 11:08
quelle
1
  

result1 sollte immer gleich result2 sein   richtig?

Falsch . Das stimmt in der Mathematik, aber nicht in Fließkommarithmetik .

Sie müssen einige Numerical Analysis-Primer lesen.

    
vartec 30.03.2009 11:16
quelle
1

Warum die Fehler je nach Reihenfolge nicht gleich sind, kann mit einem anderen Beispiel erklärt werden.

Nehmen wir an, dass es für Zahlen unter 10 alle Nummern speichern kann, also 1, 2, 3 usw. bis zu 10 speichern kann, aber nach 10 kann es nur jede zweite fällige Zahl speichern zum internen Verlust der Präzision, mit anderen Worten, es kann nur 10, 12, 14, etc. speichern.

In diesem Beispiel sehen Sie, warum das Folgende zu unterschiedlichen Ergebnissen führt:

%Vor%

Das Problem mit Fließkommazahlen besteht darin, dass sie nicht genau dargestellt werden können und der Fehler nicht immer gleich ist, daher spielt die Reihenfolge eine Rolle.

Zum Beispiel könnte 3.00000000003 + 3.00000000003 am Ende 6.00000000005 (Hinweis nicht 6 am Ende) sein, aber 3.00000000003 + 2.99999999997 könnte am Ende 6.00000000001 sein, und damit:

%Vor%

, aber ändern Sie die Reihenfolge:

%Vor%

Es wird also eine Rolle spielen.

Nun können Sie natürlich Glück haben, dass die obigen Beispiele sich gegenseitig ausgleichen, indem der erste um .xxx1 und der andere um .xxx1 nach oben schwingt, was Ihnen in beiden Fällen .xxx3 gibt, aber es gibt keine Garantie.

    
quelle
0

Sie verwenden nicht die gleichen Werte, da die Zwischenergebnisse unterschiedlich sind:

%Vor%

Da Doppelwerte nicht genau die Dezimalwerte darstellen können, erhalten Sie unterschiedliche Ergebnisse.

    
laktak 30.03.2009 11:18
quelle