Why printf prints the last number?

EDIT: I already knew that printf is not typical. I'm just looking for an explanation of exactly what happened (I mean the description of undefined behavior).

Why, if I print "7" in the second printf, the program prints 9.334354. I know that if I do not write 7.0, it will not be printed, but why should I take the first number instead?

#include <stdio.h> int main() { printf("%.2f\n", 9.334354); printf("%.5f\n", 7); printf("%03d\n", 9); getchar(); } 

This is the conclusion.

  9.33 9.33435 009 
+4
source share
6 answers

Repeat this for yourself for two weeks once a night before bedtime:

printf is not typical. printf is not typical. printf is not typical.

A function will only work if you pass it an argument of the type that you promise. Everything else is undefined behavior. You promise double (via %f ), but specify int (literal type 7 ), so this behavior is undefined. Shame on you.

(I did go into details once to explain the actual conclusion, if you're interested.)


Update. Since you are interested in explaining this specific behavior, here is the (relevant) assembly for this code on my x86 / GCC4.6.2 / -O3:

First data sections:

 .LC0: .long 1921946325 .long 1076013872 // 0x 4022AB30 728E92D5 is the binary rep of 9.334354 .LC1: .string "%.2f\n" .LC2: .string "%.5f\n" .LC3: .string "%03d\n" 

Now the code:

  fldl .LC0 // load number into fp register fstpl 4(%esp) // put 64-bit double on the stack movl $.LC1, (%esp) // first argument (format string) call printf // call printf movl $7, 4(%esp) // put integer VA (7) onto stack movl $.LC2, (%esp) // first argument (format string) call printf // call printf movl $9, 4(%esp) // put integer VA (9) onto stack movl $.LC3, (%esp) // first argument (format string) call printf // call printf 

The reason you see what you see now is simple. Let it momentarily switch to the full 17-digit output:

  printf("%.17f\n", 9.334354); printf("%.17f\n", 7); 

We get:

 9.33435399999999937 9.33435058593751243 

Now replace the integer with the β€œcorrect” binary component:

  printf("%.17f\n", 9.334354); printf("%.17f\n", 1921946325); 

And voila:

 9.33435399999999937 9.33435399999999937 

It happens that double occupies 8 bytes on the stack, the value is 0x4022AB30728E92D5 . An integer takes only 4 bytes, and as it happens, the least significant four bytes are overwritten, so the floating point value is still almost the same. If you overwrite four bytes with the same bytes as in the original float, you will get the exact result.

I could add that it is just luck that the most significant four bytes remain untouched. In different circumstances, they could be rewritten by something else. In short, "undefined behavior".

+16
source

You are using the wrong format specifiers. You pass a float (which turns into a double), so printf expects 8 bytes on the stack, but you pass an int , which is 4 bytes. Write 7.0 and 9.0 to turn literals into doubles. Or, as Daniel said in the comments, the optimizer can cause all kinds of weird behavior.

+2
source

As Barry Brown hinted, your 7 overwrites 4 bytes of the 8-byte double previously stored on the stack. Depending on how the stack grows and the content of your paired, 7 may simply overwrite the least significant bits of the double mantissa. Therefore, the double in the stack is not actually identical to that in the previous printf () call; it's just that the difference is not visible in the% .5f format.

Printf () is not strictly typical, but some compilers check the formatting operator for arguments and warn you if you do something like this.

+2
source

You tell printf that you are passing it a double (due to the format specifier f ), but you are actually passing it an int. This is undefined behavior , and literally anything can happen.

+1
source

7 is an int, but printf expects the argument to be a float. The argument from the previous printf (9.334354) is still sitting on the stack, but only one is not large enough to overwrite it.

If you change 7 to 7.0, it works correctly.

+1
source

7 is an integer. 7.0 is float / double.

printf not typical, so it expects a double, but you pass it an integer. This means undefined behavior.

0
source

Source: https://habr.com/ru/post/1379740/


All Articles