Have you ever wondered why your application never crashes, shows strange behavior, or does not print the correct double precision numbers, when you write something like
float f = 1.1f; double d = 1.1; printf("float = %f\n", f); printf("double = %f\n", d);
This would print following result
float = 1.100000 double = 1.100000
and works like you normally expect it.
But then why the question above? The %f
format specifier for the printf/scanf class of functions denotes a variable of type float
which consumes 32 bits. In the example above d
is of type double
which uses 64 bits and actually requires %lf
. The provided argument d
(64 bits) would be interpreted as a variable of type float
(32 bits). If this would happen, then the output would look more like that (at least on architectures using little endian byte order):
float = 1.100000 double = -0.000000
The double
variable d
is stored in memory as:
0x9a 99 99 99 99 99 f1 3f
As little endian byte order is used d
will finally be interpreted as:
0x3f f1 99 99 99 99 99 9a
When only the first four bytes would be considered by printf
then these bytes would be 0x9a 99 99 99
(from memory) and interpreted as float
which would represent the number (ca.) -1.58818684e-23
(0x99 99 99 9a
). Printed with %f
gives -0.000000
. But nothing of this happens and everything works like expected.
The reason is that ONLY the printf
class of functions treat %f
as %lf
, which denotes a double precision
argument. Accordingly this is also done for %g
and %e
. So why can printf
safely do this?
The reason are the default argument promotions from C. During this following casts take place (without guarantee to be complete):
char, short |
to | int |
unsigned char, unsigned short |
to | unsigned int |
float |
to | double |
The promotions are applied to
- all parameters of a function, when no prototype for this function exists and
- to all non-declared parameters of variadic functions, i.e. functions which take a variable number of arguments.
As the printf
class of functions are variadic (denoted by the elipsis „…“ in the function signature) the default argument promotions take place. Finally the call printf("float = %f\n", f)
will after promotion look like printf("float = %f\n", (double)f)
and printf
can safely assume that every %f, %g, and %e
denotes a parameter of type double
.
References:
- Michael L. Overton. Numerical Computing with IEEE Floating Point Arithmetic, SIAM, 2001. Chapter 10: Floating Point in C.
- C Standard. Chapter 6.5.2.2 (http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf)
- Stackoverflow – Default argument promotions in C function calls (http://stackoverflow.com/questions/1255775).