2013-07-07 5 views
5
printf("line 5: %f\n",98); //output is 0.000000 
printf("line 6: %f\n",98.98); //output is 98.980000 
printf("line 5: %f\n",98);//though same as first printf statement but output is 98.979980 

Obwohl erste und letzte printf-Anweisungen genau gleich sind, aber ihre Ausgabe unterscheidet. Warum?Warum arbeiten der erste und der dritte Druck so unterschiedlich?

weil ein int an printf übergeben wird, wenn es Float erwartet, deshalb funktionierte es komisch. Aber mein Punkt ist, warum in der letzten Druckanweisung, anstatt einen Müllwert oder 0 zu drucken, es den Wert der 2. printf-Anweisung verwendet, und das wird was gedruckt.

+0

http://codepad.org/P6wolXsM –

+2

@hbrock, der Compiler spielt keine Rolle. OP-Code verursacht undefiniertes Verhalten. Diese Frage ist ein Duplikat von Hunderten von Malen. –

+0

weil ein int an printf übergeben wird, wenn es float erwartet, deshalb funktionierte es komisch. Aber mein Punkt ist, warum in der letzten Druckanweisung, anstatt einen Müllwert oder 0 zu drucken, es den Wert der 2. printf-Anweisung verwendet, und das wird was gedruckt. –

Antwort

5

Wie andere haben bereits gesagt, ein int zu printf vorbei, wenn es eine double Ursachen undefiniert Verhalten erwartet, und alles, was passieren könnte. Vielleicht interessiert dich der Grund warum das Programm druckt 98.979980 auf der dritten Zeile und nicht irgendeine Zufallszahl.

Argumente werden printf auf dem Stapel übergeben. Wenn die Leitung 2 98.98 an printf übergibt, wird sie auf den Stapel geschoben, wobei der niedrigstwertige Teil der Nummer zuerst ist.

Dann printf kehrt zurück, und in der dritten Zeile wird es erneut aufgerufen, jetzt mit 98 auf den Stapel geschoben. Auf Ihrer Architektur scheint der int Typ 32 Bits zu sein; halb so groß wie der Typ double, so überschreibt dies nur die untere Hälfte von 98.98, die früher auf dem Stapel war. Die obere Hälfte von 98,98 ist noch auf dem Stapel.

Jetzt der dritte Aufruf an printf liest eine double aus dem Stapel. Die wichtigste Hälfte dessen, was es liest, kommt von der 98.98, die früher auf dem Stapel war, und die weniger signifikante Hälfte kommt von der binären Darstellung von 98; deshalb liegt das Ergebnis so nahe bei 98.98. Da 98 eine so kleine Zahl ist, sind die höchstwertigen Bits 0, und wenn Sie die niedrigstwertige Hälfte von 98.98 auf Nullen setzen, erhalten Sie eine kleinere Zahl.

Wenn Zeile 3 eine Zahl verwendet, die mehr Bits auf 1 gesetzt hat, erhalten Sie ein Ergebnis, das mehr als 98.98 ist. Zum Beispiel hat die binäre Darstellung von -1 alle seine Bits auf 1 gesetzt, und Sie erhalten:

printf("line 2: %f\n", 98.98); # 98.98 
printf("line 3: %f\n", -1); # 98.980042 

Wenn der Compiler 64 Bit ints verwendet oder weitergegeben double s mit dem wesentlichsten Teil zuerst, oder einen gebrauchten Registrieren Sie anstelle des Stapels, um Parameter zu übergeben, Sie würden sehr unterschiedliche Ergebnisse erhalten.

4

Da Ihr Programm undefined Verhalten aufruft.98 ist vom Typ int, aber %f erwartet eine float (oder double, aufgrund von Standard-Promotion-Regeln).

Also, da printf() UB hat, wenn die Arten der Konvertierung Spezifizierer und die tatsächlichen Typen nicht übereinstimmen, gibt es keine vernünftige Erklärung für irgendetwas, was es tut.

2

Dies ist, weil %f erfordert einen doppelten Parameter. Geben ein int ist undefiniertes Verhalten.

ISO/IEC 9899:1999, §7.19.6.1, 9:

Wenn ein Argument nicht der richtige Typ für die entsprechende Umwandlung Spezifikation ist, ist das Verhalten undefiniert.

Undefined behavior bezieht sich auf Computercode, dessen Verhalten unvorhersehbar ist.

Mindestens mit gcc erhalten Sie eine Warnung erhalten entsprechend, wenn Warnungen aktiviert sind:

Warnung: Format '% f' erwartet 'double' geben, aber Argument 2 hat Typ 'int'

2

%f erwartet double, aber Sie übergeben einen int Wert. Es ist undefiniertes Verhalten.

wäre die richtige sein:

printf("line 5: %f\n",98.0); 
printf("line 6: %f\n",98.98); 
printf("line 5: %f\n",98.0); 
1

Wenn wir den Code zu buchen, der Compiler folgenden sehen wir die produziert:

00401B5E|>MOV DWORD PTR SS:[ESP+0x4],0x62       ; ||| 
00401B66|>MOV DWORD PTR SS:[ESP],arma_sto.00404024     ; |||ASCII "line 5: %f\n" 
00401B6D|>CALL <JMP.&msvcrt.printf>        ; ||\printf 
00401B72|>MOV DWORD PTR SS:[ESP+0x4],0x51EB851F     ; || 
00401B7A|>MOV DWORD PTR SS:[ESP+0x8],0x4058BEB8     ; || 
00401B82|>MOV DWORD PTR SS:[ESP],arma_sto.00404030     ; ||ASCII "line 6: %f\n" 
00401B89|>CALL <JMP.&msvcrt.printf>        ; |\printf 
00401B8E|>MOV DWORD PTR SS:[ESP+0x4],0x62       ; | 
00401B96|>MOV DWORD PTR SS:[ESP],arma_sto.00404024     ; |ASCII "line 5: %f\n" 
00401B9D|>CALL <JMP.&msvcrt.printf>        ; \printf 

Weil Sie nicht die beiden 98-Werte als Schwimmer hergegeben, Die Ausgabe ist zufällig (basierend auf dem Stapel). Eine gültige Eingabe für% f ist eine Fließkommazahl, die zwei Einträge auf dem Stapel benötigt.

Zeile 4 funktioniert nicht, weil Sie nur einen Stapeleintrag bereitgestellt haben.

Leitung 5 arbeitet gut, weil 98,98 ist eine schwimmende Zeigernummer (die zwei Stapeleinträge nimmt)

Linie 6 Ausgänge ~ 98.98 da der MOV bei 00401B7A nicht rückgängig gemacht. Dies bedeutet, dass Zeile 6 einen gültigen Gleitkommawert ausgibt, da sie zwei Stapeleinträge hat, aber die falsche Gleitkommazahl, da ein Teil von der vorherigen Zahl übrig ist.

Lösung, Guss als Schwimmer

printf("line 5: %f\n",(float)98); //output is 98.000000 
printf("line 6: %f\n",98.98); //output is 98.980000 
printf("line 5: %f\n",(float)98); //output is 98.000000 
Verwandte Themen