Verschiedene Antworten, indem eine printf-Anweisung entfernt wird

8

Dies ist der Link zur Frage nach UVa Online-Juroren.
Ссылка

Mein C-Code ist

%Vor%

Einer der Eingänge und Ausgänge sind
Eingabe von
3
0,01
0.03
0.03
0

Ausgabe
$ 0,01

Meine Ausgabe ist
0,00 €.

Allerdings, wenn ich die Zeile printf ("% lf", Durchschnitt) auskommentieren;

Die Ausgabe ist wie folgt
0.02 // Das ist der durchschnittliche
0,01 $

Ich führe den Code auf ideone.com

Bitte erklären Sie, warum das passiert.

    
Aditya Sharma 13.06.2015, 16:15
quelle

3 Antworten

8

Ich glaube, ich habe den Schuldigen und eine vernünftige Erklärung gefunden.

Bei x86-Prozessoren arbeitet die FPU intern mit erweiterter Genauigkeit , einem 80-Bit-Format. Alle Gleitkommabefehle arbeiten mit dieser Genauigkeit. Wenn ein double tatsächlich benötigt wird, generiert der Compiler Code, um den erweiterten Genauigkeitswert in einen doppelten Genauigkeitswert zu konvertieren. Entscheidend ist, dass die auskommentierte printf eine solche Konvertierung erzwingt, da die FPU-Register über diesen Funktionsaufruf hinweg gespeichert und wiederhergestellt werden müssen und als double s gespeichert werden (beachten Sie, dass avg und mon beide inline sind) Es passiert also kein Speichern / Wiederherstellen.)

Tatsächlich können wir anstelle von printf die Zeile static double dummy = average; verwenden, um die double Konvertierung zu erzwingen, was auch dazu führt, dass der Fehler verschwindet: Ссылка

Ihr Wert von average liegt aufgrund von Gleitkomma-Ungenauigkeiten nahe bei, aber nicht exakt 0.02 . Wenn ich alle Berechnungen explizit mit long double durchführe und den Wert von average ausdrucke, ist dies der folgende:

%Vor%

Jetzt können Sie das Problem sehen. Wenn Sie das printf hinzufügen, wird average auf ein double gezwungen, was es über 0,02 drückt. Ohne printf wird average im nativen Format mit erweiterter Genauigkeit etwas kleiner als 0,02 sein.

Wenn Sie int a=temp*100; ausführen, wird der Fehler angezeigt. Ohne die Konvertierung macht dies a = 1 . Bei der Konvertierung macht dies a = 2 .

Um dies zu beheben, verwenden Sie einfach int a=round(temp*100); - alle Ihre seltsamen Fehler sollten verschwinden.

Beachten Sie, dass dieser Fehler extrem empfindlich auf Änderungen im Code reagiert. Alles, was dazu führt, dass die Register gespeichert werden (wie zum Beispiel ein printf ziemlich überall), wird tatsächlich dazu führen, dass der Fehler verschwindet. Daher ist dies ein extrem gutes Beispiel für einen heisenbug : ein Fehler, der verschwindet, wenn Sie versuchen, ihn zu untersuchen.

    
nneonneo 13.06.2015 17:13
quelle
2

@nneonneo hat den Großteil des Problems gut beantwortet: Leicht variante Kompilationen führen zu etwas anderem Gleitkomma-Code, der dazu führt fast die gleiche double Antwort, außer eine Antwort ist knapp unter 2,0 und die andere bei oder nur etwa 2,0.

Fügen Sie etwa hinzu, wie wichtig es ist, beim Floating-Point-Runden keine Konvertierung in int zu verwenden.

Code wie double temp; ... int a=temp*100; akzentuiert diesen Unterschied in a mit einem Wert von 1 oder 2, da die Konvertierung in int effektiv ist "truncate in Richtung Null" - lösche den Bruch.

Statt auf 0,01 mit Code wie:

zu runden %Vor%

Verwenden Sie int überhaupt nicht. Verwenden Sie

%Vor%

Dies liefert nicht nur konsistentere Antworten (da temp wahrscheinlich keine Werte in der Nähe eines halben Cent ), erlaubt es auch temp -Werte außerhalb des Bereichs int . temp = 1e13; int a = temp/100; führt sicher zu undefiniertem Verhalten.

  

Verwenden Sie die Umwandlung in int nicht zum Runden von Gleitkommazahlen: Verwenden Sie round()

roundf() , roundl() , floor() , ceil() usw. können ebenfalls nützlich sein. @jeff

    
chux 13.06.2015 19:11
quelle
-4

Sie teilen ein Double durch eine ganze Zahl, die eine Integer-Division ist. In diesem Fall gibt es einen Wert von 0.

Wenn Sie den Schüler zu einem Doppelwurf machen, sollte er Ihnen eine korrekte Ausgabe geben.

%Vor%

Abhängig von Ihrer Arithmetik können andere Stellen benötigt werden.

    
Andrew Corsini 13.06.2015 16:29
quelle

Tags und Links