Consider the following code from C Primer Plus:
#include <stdio.h>
int main()
{
float f = 7.0f;
float g = 8.0f;
printf("%d %d\n", f, g); // wrong kind of values
return 0;
}
using the clang compiler produces the following output which is an expected result by the way. The compiler rightfully complains about the float type arguments that are assigned to wrong format specifiers:
~$ clang badcount.c -o badcount
badcount.c:11:23: warning: format specifies type 'int' but the argument has type 'float' [-Wformat]
printf("%d %d\n", f, g); // wrong kind of values
~~ ^
%f
badcount.c:11:26: warning: format specifies type 'int' but the argument has type 'float' [-Wformat]
printf("%d %d\n", f, g); // wrong kind of values
~~ ^
%f
2 warnings generated.
however compiling the same code on gcc 8.2.0 will produce the following output:
~$ gcc-8 badcount.c -fsingle-precision-constant -o badcount -Wall
badcount.c: In function 'main':
badcount.c:11:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
printf("%d %d\n", f, g); // wrong kind of values
~^ ~
%f
badcount.c:11:17: warning: format '%d' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
printf("%d %d\n", f, g); // wrong kind of values
~^ ~
%f
even though f
and g
are of type float and even though an f
suffix is used to explicitly make literal values of type float, gcc will still promote them into double types. I even tried the following code but still gcc ignores explicit float types and promote them into double. I looked up the gcc documentation but it looks like there are no options to prevent automatic promotion of literal floats to literal doubles, and gcc implicitly does this, and ignores f
suffixes on literals.
printf("%d %d\n", f, 45.9f);
In the above code 45.9f will still be promoted to type double even though I have implicitly made it float.
Are there any options or solutions to prevent this? Or is this intended by design in gcc?
Edit: I forgot to put this code that I tried, using an explicit cast to type float doesn't prevent this either:
printf("%d %d\n", f, (float)(45.9f));
not even this:
float f = (float)7.0f;
It seems gcc doesn't care about explicit castings either, and will promote floats to double all the same.
Let's check what your code gets translated to at the assembly level.
clang -O3 -fomit-frame-pointer -S ctest.c -o ctest1.s
LCPI0_0:
.quad 4619567317775286272
LCPI0_1:
.quad 4620693217682128896
_main:
pushq %rax
leaq L_.str(%rip), %rdi
movsd LCPI0_0(%rip), %xmm0
movsd LCPI0_1(%rip), %xmm1
movb $2, %al
callq _printf
xorl %eax, %eax
popq %rcx
retq
L_.str:
.asciz "%d %d\n"
gcc -O3 -S ctest.c -o ctest2.s
lC2:
.ascii "%d %d\12\0"
_main:
leaq lC2(%rip), %rdi
subq $8, %rsp
movl $2, %eax
movsd lC0(%rip), %xmm1
movsd lC1(%rip), %xmm0
call _printf
xorl %eax, %eax
addq $8, %rsp
ret
lC0:
.long 0
.long 1075838976
lC1:
.long 0
.long 1075576832
(Some labels and assembler directives removed for brevity)
The conversion from float to double is performed in both cases. The doubles were actually pre-computed at compile time, and are then loaded into SSE registers with movsd
.
The difference you observe is simply a choice in the way the compiler reports the error. Clang displays the type before the implicit conversion, and gcc, the type post conversion. But in both cases, the arguments passed to printf
are of type double
.