为什么即使在使用f后缀之后,gcc仍将浮点文字转换为双精度,但是clang却似乎没有?

时间:2018-11-30 22:20:38

标签: c gcc clang

请考虑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似乎也不在乎显式强制转换,并且会将浮点数提高一倍。

1 个答案:

答案 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类型。