Why does printf("%f",0); give undefined behavior?

Trevor Hickey picture Trevor Hickey · Jul 26, 2016 · Viewed 7.3k times · Source

The statement

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

prints 0.

However, the statement

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

prints random values.

I realize I'm exhibiting some kind of undefined behaviour, but I can't figure out why specifically.

A floating point value in which all the bits are 0 is still a valid float with value of 0.
float and int are the same size on my machine (if that is even relevant).

Why does using an integer literal instead of a floating point literal in printf cause this behavior?

P.S. the same behaviour can be seen if I use

int i = 0;
printf("%f\n", i);

Answer

Keith Thompson picture Keith Thompson · Jul 26, 2016

The "%f" format requires an argument of type double. You're giving it an argument of type int. That's why the behavior is undefined.

The standard does not guarantee that all-bits-zero is a valid representation of 0.0 (though it often is), or of any double value, or that int and double are the same size (remember it's double, not float), or, even if they are the same size, that they're passed as arguments to a variadic function in the same way.

It might happen to "work" on your system. That's the worst possible symptom of undefined behavior, because it makes it difficult to diagnose the error.

N1570 7.21.6.1 paragraph 9:

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

Arguments of type float are promoted to double, which is why printf("%f\n",0.0f) works. Arguments of integer types narrower than int are promoted to int or to unsigned int. These promotion rules (specified by N1570 6.5.2.2 paragraph 6) do not help in the case of printf("%f\n", 0).

Note that if you pass a constant 0 to a non-variadic function that expects a double argument, the behavior is well defined, assuming the function's prototype is visible. For example, sqrt(0) (after #include <math.h>) implicitly converts the argument 0 from int to double -- because the compiler can see from the declaration of sqrt that it expects a double argument. It has no such information for printf. Variadic functions like printf are special, and require more care in writing calls to them.