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.
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:
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 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
Dies liefert nicht nur konsistentere Antworten (da temp
wahrscheinlich keine Werte in der Nähe eines halben Cent temp
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 Sieround()
roundf()
, roundl()
, floor()
, ceil()
usw. können ebenfalls nützlich sein. @jeff
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.
Tags und Links c