为什么gcc即使在使用f后缀后也会将浮点文本转换为double,而clang似乎不会?

为什么gcc即使在使用f后缀后也会将浮点文本转换为double,而clang似乎不会?,c,gcc,clang,C,Gcc,Clang,考虑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; } 但是,在gcc 8.2.0上编译相同的代码将产生以下输出: ~$ gcc-8 badcount.c -fsingle-precision-

考虑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;
}
但是,在gcc 8.2.0上编译相同的代码将产生以下输出:

~$ 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和g是float类型,即使f后缀用于显式地生成float类型的文本值,gcc仍然会将它们升级为双类型。我甚至尝试了以下代码,但gcc仍然忽略显式浮点类型,并将它们升级为双精度浮点类型。我查找了,但似乎没有任何选项可以阻止将文本浮点自动升级为文本双精度,gcc隐式地这样做,并忽略文本上的f后缀

    printf("%d %d\n", f, 45.9f);
在上面的代码中,45.9f仍将被提升为double类型,尽管我已经隐式地将其设置为float

是否有任何选项或解决方案来防止这种情况?或者这是gcc中的设计意图

编辑:我忘了输入我尝试过的代码,使用显式转换类型float也不能防止这种情况:

printf("%d %d\n", f, (float)(45.9f));
即使是这个:

float f = (float)7.0f;
gcc似乎也不关心显式转换,并且会将浮点值提升到原来的两倍。

这两个编译器都尊重标准并生成等效代码。 让我们检查代码在程序集级别被翻译成什么

clang-O3-fomit帧指针-S ctest.c-o ctest1.S

gcc-O3-S ctest.c-o ctest2.S

为了简洁起见,删除了一些标签和汇编指令

在这两种情况下都会执行从浮点到双精度的转换。双精度实际上是在编译时预先计算的,然后用movsd加载到SSE寄存器中

您观察到的差异只是编译器报告错误的方式的选择。Clang显示隐式转换之前的类型,gcc显示转换后的类型。但在这两种情况下,传递给printf的参数都是double类型。

两个编译器都遵守标准并生成等效代码。 让我们检查代码在程序集级别被翻译成什么

clang-O3-fomit帧指针-S ctest.c-o ctest1.S

gcc-O3-S ctest.c-o ctest2.S

为了简洁起见,删除了一些标签和汇编指令

在这两种情况下都会执行从浮点到双精度的转换。双精度实际上是在编译时预先计算的,然后用movsd加载到SSE寄存器中


您观察到的差异只是编译器报告错误的方式的选择。Clang显示隐式转换之前的类型,gcc显示转换后的类型。但在这两种情况下,传递给printf的参数都是double类型。

我知道对于float和double我们使用%f,但这不是问题的重点。因为C11草案标准n1570,6.5.2.2函数调用,第6段和第7段指定varargs参数从float提升为double。@ilgaar:规则在C90、C99和C11中是相同的。float类型的可变参数升级为double,小于int的整数类型的可变参数升级为int或无符号int。。。争论应该对这没有影响。谁说克朗没有影响?@ilgaar:对于与。。。在参数中,两个编译器都将float提升为double。错误消息中的差异仅仅是选择是否描述int与原始参数类型float或升级参数类型double之间的不匹配。我知道对于float和double,我们使用%f,但这不是问题的重点。因为C11草案标准n1570,6.5.2.2函数调用,第6段和第7段指定将varargs参数从float提升为double。@ilgaar:在C90、C99和C11中的规则相同。float类型的可变参数升级为double,小于int的整数类型的可变参数升级为int或无符号int。。。争论应该对这没有影响。谁说克朗没有影响?@ilgaar:对于与。。。在参数中,两个编译器都将float提升为double。错误消息中的区别仅仅是选择是否描述int与原始参数类型float或升级参数类型double之间的不匹配。
float f = (float)7.0f;
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"
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