Reputation: 163
EDIT: I already knew that printf is not typesafe I am just looking for an explination about what exactly ocurred (I mean describe the undefined behavior).
Why if I print "7" in the second printf, the program prints 9.334354. I know that if I don't write 7.0, this won't be printed but Why does it take the first number writted instead?.
#include <stdio.h>
int main()
{
printf("%.2f\n", 9.334354);
printf("%.5f\n", 7);
printf("%03d\n", 9);
getchar();
}
This is the output
9.33
9.33435
009
Upvotes: 5
Views: 790
Reputation: 477610
Repeat this to yourself for two weeks once a night before going to bed:
printf is not typesafe. printf is not typesafe. printf is not typesafe.
The function will only work if you pass it an argument of the type that you promise. Everything else is undefined behaviour. You promise a double
(via %f
) but provide an int
(the type of the literal 7
), so it's undefined behaviour. Shame on you.
(I did once go into details to explain the actual output, in case you're interested.)
Update: Since you're interested in the explanation for this particular behaviour, here's the (relevant) assembly for that code on my x86/GCC4.6.2/-O3:
First the 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 is simple now. Let's for a moment switch to full 17-digit output:
printf("%.17f\n", 9.334354);
printf("%.17f\n", 7);
We get:
9.33435399999999937
9.33435058593751243
Now let's replace the integer by the "correct" binary component:
printf("%.17f\n", 9.334354);
printf("%.17f\n", 1921946325);
And voila:
9.33435399999999937
9.33435399999999937
What happens is that the double
occupies 8 bytes on the stack, of value 0x4022AB30728E92D5
. The integer only occupies 4 bytes, and as it happens, the least significant four bytes are overwritten, so the floating point value is still nearly the same. If you overwrite the four bytes with the same bytes that occur in the original float, then you get the exact same result.
I might add that it's pure luck that the most significant four bytes remain intact. In different circumstances, they might have been overwritten with something else. In short, "undefined behaviour".
Upvotes: 16
Reputation: 231
As Barry Brown hinted at, your 7 is overwriting 4 bytes of the 8-byte double previously stored on the stack. Depending on which way the stack grows and the endianness of your doubles, the 7 might just be overwriting the least significant bits of the double's mantissa. Consequently, the double on the stack is not actually identical to that in the previous call to printf(); it's just that the difference is not visible in the %.5f format.
Printf() is not strictly typesafe, but some compilers check the format statement against the arguments and will warn you if you do something like this.
Upvotes: 2
Reputation: 9940
7
is an integer.
7.0
is a float/double.
printf
isn't typesafe so it's expecting a double but you're passing it an integer. That means undefined behavior.
Upvotes: 0
Reputation: 20634
7 is an int, but printf is expecting the argument to be a float. The argument from the previous printf (9.334354) is still sitting on the stack and the 7 alone isn't big enough to overwrite it.
If you change the 7 to 7.0, it works correctly.
Upvotes: 1
Reputation: 75150
You're using the wrong format specifiers. You're passing a float (which turns into a double) so printf
is expecting 8 bytes on the stack, but you pass an int
, which is 4 bytes. Write 7.0
and 9.0
to turn the literals into doubles. Or, as Daniel said in the comments, the optimiser could cause all sorts of weird behaviour to occur.
Upvotes: 2
Reputation: 60997
You're telling printf
that you're passing it a double
(because of the f
format specifier) but you're actually passing it an int. That's undefined behavior, and literally anything could happen.
Upvotes: 1