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

Though first and last printf statements are exactly same, but their output differs. Why?

beacuse an int is being passed to printf when it is expecting float, thats why it worked weird. But my point is why in the last print statment, instead of printing some garbage value or 0, it is using the value of 2nd printf statement and thats what gets printed.

Ashish Saini
  • 117
  • 6
  • http://codepad.org/P6wolXsM โ€“ 1'' Jul 07 '13 at 17:26
  • 2
    @hbrock, the compiler doesn't matter. OP's code causes undefined behaviour. This question is a duplicate hundreds of times over. โ€“ Carl Norum Jul 07 '13 at 17:26
  • beacuse an int is being passed to printf when it is expecting float, thats why it worked weird. But my point is why in the last print statment, instead of printing some garbage value or 0, it is using the value of 2nd printf statement and thats what gets printed. โ€“ Ashish Saini Jul 09 '13 at 02:48

5 Answers5

5

As others have already said, passing an int to printf when it's expecting a double causes undefined behaviour, and anything could happen. You might be interested in the reason why the program prints 98.979980 on the third line and not some random number.

Arguments are passed to printf on the stack. When line 2 passes 98.98 to printf it is pushed on the stack, with the least significant part of the number first.

Then printf returns, and on the third line it is called again, now with 98 pushed on the stack. On your architecture the int type seems to be 32 bits; half the size of the double type, so this overwrites only the lower half of 98.98 that was on the stack earlier. The upper half of 98.98 is still on the stack.

Now the third call to printf reads a double from the stack. The most significant half of what it reads comes from the 98.98 that was on the stack earlier, and the less significant half comes from the binary representation of 98; this is why the result is so close to 98.98. Since 98 is such a small number its most significant bits will be 0, and setting the least significant half of 98.98 to mostly zeros gives you a smaller number.

If line 3 used a number that has more bits set to 1 you would get a result that is more than 98.98. For example, the binary representation of -1 has all its bits set to 1, and you get:

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

If the compiler used 64 bit ints, or passed doubles with the most significant part first, or used a register instead of the stack to pass parameters, you would get very different results.

Joni
  • 108,737
  • 14
  • 143
  • 193
4

Because your program invokes undefined behavior. 98 is of type int, but %f expects a float (or double, due to default promotion rules).

So, since printf() has UB when the types of the conversion specifiers and the actual types mismatch, there's no reasonable explanation for anything it does.

3

This is because %f requires a double parameter. Giving an int is undefined behavior.

ISO/IEC 9899:1999, ยง7.19.6.1, 9:

If any argument is not the correct type for the corresponding conversion specification, the behavior is Undefined.

Undefined behavior refers to computer code whose behavior is unpredictable.

At least with gcc you will get a warning accordingly if warnings are enabled:

warning: format '%f' expects type 'double', but argument 2 has type 'int'

Community
  • 1
  • 1
undur_gongor
  • 15,657
  • 5
  • 63
  • 75
2

%f expects double, but you are passing an int value. It's undefined behaviour.

The proper would be:

printf("line 5: %f\n",98.0); 
printf("line 6: %f\n",98.98); 
printf("line 5: %f\n",98.0);
nullptr
  • 11,008
  • 1
  • 23
  • 18
2

If we look at the code the compiler produces we see the following:

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

Because you didn't cast the two 98 values as float, the output is random (based on the stack). A valid input for %f is a floating pointer number, which takes two entries on the stack.

line 4 doesn't work because you only provided one stack entry.

line 5 is working fine, because 98.98 is a floating pointer number (which takes two stack entries)

line 6 outputs ~98.98 because the MOV at 00401B7A isn't undone. This means that line 6 outputs a valid float because it has two stack entries, but the wrong floating pointer number because there is a part left from the previous number.

Solution, cast as float

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
mrexodia
  • 648
  • 12
  • 20