0

The o/p of the code snippet:

printf("%f", 9/5);
  1. in my linux gcc 4.6.3 (gcc myprog.c followed by ./a.out):

    -0.000000

  2. in codepad.org:

    2.168831

Why the difference?

I have referred the links: Why cast is needed in printf? and Implicit conversion in C?, but could'nt make use of it.

Info regarding codepad execution: C: gcc 4.1.2 flags: -O -fmessage-length=0 -fno-merge-constants -fstrict-aliasing -fstack-protector-all

EDIT: More: for the execution of following in (in same program) codepad

printf("%f\n", 99/5);
printf("%f\n", 18/5);
printf("%f\n", 2/3);
printf("%f\n%f\n%f", 2, 3, 4);
printf("%f\n", 2);

the o/p is

2.168831
2.168831
2.168831
0.000000
0.000000
-0.001246
-0.0018760.000000

The first three outputs are same garbage values (and not the last one). Wondering why.

Community
  • 1
  • 1
Karthik
  • 365
  • 1
  • 3
  • 16

3 Answers3

4

9/5 is seen as an int .. which leads to undefined output when you use the format specifier %f ( which expects a double )

to print correct result do this:

printf("%f", 9.0/5); // forces system to make sure that the argument is not treates as int

or

printf("%f\n",(double)9/5); // type casting the argument to a double .. this is more helpful 
                            // in case you have a variable

I am not sure how codepad.org compiles their code .. but for gcc you have to have correct arguments for the printf to match with the format specifier


Consider the following to see this more carefully:

#include <stdio.h>
int main(){ 
    printf("%f %d %f\n",9/5,9/5,9.0/5);
    printf("%f\n",1); // as suggested by devnull-again since the argument is not a double
                      // the output is undefined
    return 0;
}

Output:

$ ./test
0.000000 1 1.800000
0.000000
sukhvir
  • 5,265
  • 6
  • 41
  • 43
1

It's undefined behavior, anything could be printed at all. 9/5's type is int. %f requires a double to be passed (if you pass a float, it will be converted to double thanks to automatic promotion of arguments to variadic function calls, so that's ok too).

Since you're not giving a double, anything can happen.

In practice, assuming sizeof(int)==4 and sizeof(double)==8, your printf call will read 4 bytes of random garbage. If that's all zeros, then you'll print zero. If it's not, you'll print random stuff.

Mat
  • 202,337
  • 40
  • 393
  • 406
  • printf call will read 4 bytes of random garbase => thanks for the info. – Karthik Nov 10 '13 at 11:40
  • @Mat - IMHO you are wrong - what printf will take from the stack is determined by format string and on the stack regular int is pushed not double. – Artur Nov 10 '13 at 11:42
  • @Artur: that's exactly what I say. printf will read a double's worth when all that is passed is an int. So it will read garbage – Mat Nov 10 '13 at 11:43
  • @Mat: It does not have to be all zero bits to print (mean zero). Everything with exponent=0 must be printed as zero - if it is not - the compiler does not know how to interpret floating point correctly. For example: **printf("%f", 0x12340000);** must print 0.0 as according to standard it must be treated as zero. Thousands of other patterns must mean zero too. – Artur Nov 10 '13 at 12:02
  • @Artur: printf("%f", 0x12340000); => executing in gcc 4.1.2 (codepad.org) throws o/p as 2.168831. any idea? – Karthik Nov 10 '13 at 12:10
  • @Artur: The C standard does not require that arguments be passed on the stack or that `int` arguments be passed in the same locations as `double` arguments. Passing an `int` argument for a `double` format specifier may result in the `int` being passed in a general register but `printf` reading completely unrelated data from a floating-point register. Even on systems where integer and floating-point arguments are passed in the same places, optimization and other processing by the C implementation may result in other behaviors. – Eric Postpischil Nov 10 '13 at 12:11
0

Codepad is wrong. Every number is int if stated otherwise - that is the key to solution.

In C/C++ 9/5 is int number and equals 1 That is why your :

printf("%f\n", 9 / 5);

is the same as:

printf("%f\n", 1);

But why it prints 0.0 you ask - here is why. When printf is given '%f' flag it will treat a parameter as float.

Now your code disassembly looks / may look like that:

printf("%f\n", 9 / 5);
push        1    
call        dword ptr [__imp__printf (0B3740Ch)]

push 1 moves (usually 32bit 0x1) on the stack

And now the most important how 32 bit value=1 looks (in binary):

(MSB) 00000000 00000000 00000000 00000001 (LSB)

and what this pattern means if treated as 32 bit floating point (according to this):

sign(bit #31) = 0

exponent(next 8 bits ie #30..#23) all = 0 too

fraction(the remaining bits) contains 1(dec)

enter image description here

This is in theory BUT exponent=0x0 which is special case (read link) and is treated as 0.0 - period.

Community
  • 1
  • 1
Artur
  • 7,038
  • 2
  • 25
  • 39
  • Explanation using disassembly was really helpful. @Mat has additional information. – Karthik Nov 10 '13 at 11:39
  • Code pad is not wrong. The C standard does not define the behavior when the type of an argument does not match its format specifier. It is not correct to expect that the bytes of an `int` argument will be interpreted as a `float` or `double`. – Eric Postpischil Nov 10 '13 at 12:05
  • IMHO it is perfectly correct - "%f" format specifier is for printf to let it know how it should treat values on the stack (if cdecl used). Printf just does not know what there is on the stack - it must trust user. If user says "%f" there is nothing else printf can do apart from taking exactly 4 bytes from the stack and interpreting them as float. If it cannot interpret 4 byte pattern correctly as float this particular printf is just coded wrong. Not that I am big fan of VC but VC's printf works perfectly fine with all situations shown in the question. – Artur Nov 10 '13 at 12:09
  • @Artur: C implementations are not required to use a stack to pass arguments nor are required to pass integer and floating-point arguments in the same places. Additionally, when the argument is the wrong type for its corresponding format specifier, the C standard permits **any** behavior. The compiler is permitted to observe the mismatch and generate **any** code as a result. – Eric Postpischil Nov 10 '13 at 12:15
  • Additionally, the math in this answer shows interpretation of the bits as a 32-bit binary floating-point number, but `%f` is for `double`, which is commonly 64 bits. – Eric Postpischil Nov 10 '13 at 12:16
  • C 2011 (N1570) 7.21.6.1 9: “If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.” – Eric Postpischil Nov 10 '13 at 12:18
  • @Eric: I agree but where is in (int printf ( const char * format, ...)) any argument (after format) with states type explicitly? There is none. So what is pushed on stack or passed down to printf in any other way (depending on call conv/arch...) will be interpreted by printf solely based on its first argument. What printf sees depends on conversions, promotions.... So if printf gets "%f" it interpres such arg as (floating point). If that is 4 or 8 bytes is another story depending on where you look cplusplus.com/msdn/etc or which compiler you use or to which srchitecture you compile. – Artur Nov 10 '13 at 13:09
  • @Artur: The rules for how arguments are passed to a function with “...” in its parameter list are in C 2011 6.5.2.2 7: “The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments” and 6.5.2.2 6: “If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.” – Eric Postpischil Nov 10 '13 at 13:58
  • @Artur: Additionally, your hypothesis that the behavior is caused by Codepad’s `printf` interpreting the bytes of `int` 1 as a floating-point value is untenable because the output for the `int` 1 argument is “2.168831”, so your hypothesis would imply that when the floating-point argument 0x1.00000001p-1042 is passed to `printf` for `%f`, Codepad also prints “2.168831”, since the bytes used to represent 0x1.00000001p-1042 are also 0x00000001 and 0x00000001 (grouped as two four-byte `int` objects). This does not occur; therefore your hypothesis is false. – Eric Postpischil Nov 10 '13 at 14:03