请考虑C Primer Plus中的以下代码:
select country.country, count(customer_id) as Clients from customer
INNER JOIN address on customer.address_id = address.address_id
INNER JOIN city on address.city_id = city.city_id
INNER JOIN country on city.country_id = country.country_id
GROUP BY country.country
order by Clients DESC
limit 5;
使用clang编译器会产生以下输出,这是预期的结果。编译器正确地抱怨分配给错误格式说明符的float类型参数:
#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;
}
但是在gcc 8.2.0上编译相同的代码将产生以下输出:
~$ 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.
即使~$ 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
和f
的类型为float,即使使用后缀g
显式地使类型的字面值成为float类型,gcc仍会将它们提升为double类型。我什至尝试了以下代码,但gcc仍然忽略了显式的float类型并将其提升为double类型。我抬起头来gcc documentation,但似乎没有选择来阻止将文字浮点数自动提升为文字双精度数,而gcc隐式地这样做了,并且忽略了文字上的f
后缀。
f
在上面的代码45.9f中,即使我隐式使它浮动,它仍将提升为double类型。
是否有防止这种情况的选项或解决方案?还是这是gcc设计的目的?
编辑:我忘了输入我尝试过的这段代码,使用显式强制类型转换来输入float还是不能防止这种情况:
printf("%d %d\n", f, 45.9f);
甚至没有:
printf("%d %d\n", f, (float)(45.9f));
gcc似乎也不在乎显式强制转换,并且会将浮点数提高一倍。
答案 0 :(得分:3)
让我们检查一下您的代码在汇编级转换为什么。
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
(为简便起见,删除了一些标签和汇编器指令)
在两种情况下都执行从float到double的转换。实际上,双精度是在编译时预先计算的,然后使用movsd
加载到SSE寄存器中。
您观察到的差异只是编译器报告错误的一种选择。 Clang在隐式转换之前显示类型,在转换后显示gcc。但是在两种情况下,传递给printf
的参数都是double
类型。