2

I executed the following code

#include <stdio.h>

int main()
{
    printf("%f\n", 9/5);
}

Output : 0.000000

why not 1 ?

if i write printf("%f %f %d %d\n", (float)9/5, 4, sizeof(float), sizeof(int));

then output is 1.800000 0.000000 4 59

why not 1.800000 4 4 4

on my machine the sizeof (float) is 4

Thanks in advance

mskfisher
  • 3,291
  • 4
  • 35
  • 48
chinmayaposwalia
  • 243
  • 5
  • 13

6 Answers6

12

This is because your printf format specifier doesn't match what you passed it:

9/5 is of type int. But printf expects a float.

So you need to either cast it to a float or make either literal a float:

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

As for why you're getting 0.0, it's because the printf() is reading the binary representation of 1 (an integer) and printing it as a float. Which happens to be a small denormalized value that is very close to 0.0.

EDIT : There's also something going with type-promotion on varargs.

In vararg functions, float is promoted to double. So printf() in this case actually expects a 64-bit parameter holding a double. But you only passed it a 32-bit operand so it's actually reading an extra 32-bits from the stack (which happens to be zero in this case) - even more undefined behavior.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 1
    And when asked for warnings, a compiler like GCC would have found that (use `gcc -Wall`) – Basile Starynkevitch Nov 05 '11 at 17:40
  • There's no guarantee that the arguments to printf are passed on the stack (or even that there is a stack). You don't know *what* it's reading. – Stephen Canon Nov 05 '11 at 18:05
  • @Stephen Canon: What are alternatives? Could give an example? – jfs Nov 05 '11 at 18:59
  • It doesn't matter. Undefined behavior is undefined behavior. As long as your types match the format strings, there's nothing to worry about. In C++, it's easier (and safer) to just use "cout". – Mysticial Nov 05 '11 at 19:15
  • @J.F.Sebastian: sure, the arguments can be passed in register, as in the standard x86_64 ABI. – Stephen Canon Nov 05 '11 at 19:30
  • @Mysticial: right, undefined behavior is undefined behavior. I'm just pointing out that your explanation of "what really happens" doesn't necessarily hold. – Stephen Canon Nov 05 '11 at 19:33
  • @Stephen Canon: and what it reading in this case? – jfs Nov 05 '11 at 19:34
  • @J.F.Sebastian: That's impossible to say without additional experiments. The observed behavior is consistent with passing the arguments on the stack, but it would also (for example) be consistent with the ARM calling conventions (where the args would be passed partially on the stack and partially in register), or any number of other situations. – Stephen Canon Nov 05 '11 at 19:40
  • @Stephen Cannon: I'm tempted to think that varargs have to be passed on the stack otherwise printf won't "know" where the parameters are. For example integers could be in normal registers while floating point could be in the FPU stack or SSE register. So basically it would wreck havoc on the calling convention unless everything is just thrown on the stack. I might be wrong though. – Mysticial Nov 05 '11 at 19:57
  • @Mysticial: On OS X or Linux on x86_64, even va_args are passed in register (I'm not certain about the Windows ABI). I know of several other platforms that do similar things. They needn't be placed on the stack; all that's required is that printf be able to figure out where to find them (either using the format string, or some other means). – Stephen Canon Nov 05 '11 at 21:26
3

Let's look at what is going on at the bit level:

  • You compute 9/5, both numbers int -> This evaluates to 1 (again int) (let's say 32 bits) which is:

    0000 0000 0000 0000 0000 0000 0000 0001
    
  • You push it as argument of printf

  • You tell printf to get 32 bits of data from arguments and print it as a floating point number.
  • printf reads that number I wrote above and prints it as if it was encoded with IEEE 754. The result is almost 0.

You can see here the conversion:

0000 0000 0000 0000 0000 0000 0000 0001

evaluates to

1.4012984e-45

As to why

printf("%f %f %d %d\n", (float)9/5, 4, sizeof(float), sizeof(int));

doesn't produce what you expect, the reason is that the argument 4 is an int, which is 4 bytes (on your machine). Due to argument promotion, %f expects a double which is 8 bytes. It's the same problem as above.

Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • but when i use float b = 9/5 and then print the value of b, why i does not do the same. Its printing 5 in that case. Can you please explain !! – chinmayaposwalia Nov 06 '11 at 10:34
  • 5?! That's impossible. You need to show me a piece of code that does that and I'll tell you – Shahbaz Nov 07 '11 at 09:06
0

Using incorrect format specifiers inside printf is UB.

 printf("%f %f %zu %d\n", (float)9/5, 4.0, sizeof(float), sizeof(int));

gives the correct output.

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
0

If you build with GCC, you should turn on all the warnings with -Wall and read them carefully.

The reason you get "strange" output is that you've violated printfs preconditions (passing arguments whose types do not match the format spec), and once you did, printf is free to do whatever it wants, including crashing, printing garbage, or melting your CPU.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
0

9/5 is an integer but you are trying to print it with "%f". It is the source of the problem. Switch to either 9.0/5 or "%d" and you will get the right answer.

xis
  • 24,330
  • 9
  • 43
  • 59
0

(float)9/5 is casting only 9 not the result of 9/5. So the / operation isn't an integer operation at this point. (float)(9/5) will give the result 1.00000.

Try compiling with all warnings, and it will probably tell you a lot of what is wrong.

For example on a 64-bit linux system, compiling with gcc -Wall I get:

pf.c: In function ‘main’:
pf.c:6:2: warning: format ‘%f’ expects argument of type ‘double’, but argument 3 has type ‘int’ [-Wformat]
pf.c:6:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat]
pf.c:6:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 5 has type ‘long unsigned int’ [-Wformat]

As mentioned above you need the correct format specifiers. Using the right format specifiers and casting gives:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    printf("%f %f %lu %lu\n",(float)(9/5),(float)4,sizeof(float),sizeof(int));
    return 0;
}

gives:

snits@perelman:~/proj/c=>./pf
1.000000 4.000000 4 4