通过C语言中的整数转换进行浮点数比较

Bar*_*jak 5 c floating-point comparison gcc

我想得到一个确切/准确的答案,为什么下面的代码打印出不同的结果:

#include "stdio.h"
int main(void)
{
    int a = 9;
    int b = 10;
    printf("%d\n", (double)a / (double)b == 0.9); /* prints 0 */
    printf("%d\n", (double)9 / (double)10 == 0.9); /* prints 1 */
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我认为这可能是编译器依赖的,我的是gcc(GCC mingw Windows7)4.8.1和gcc(Debian 4.7.2-5)4.7.2.

非常感谢你!

UPDATE!

我使用和不使用-std = c99选项生成汇编代码,这应该有助于理解这里发生的事情.

没有-std = c99(这给出了结果0/1):

    .file    "a.c"
    .section .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string  "%d\n"
    .section .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl   main
    .type    main, @function
main:
.LFB11:
    .cfi_startproc
    pushl %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl %esp, %ebp
    .cfi_def_cfa_register 5
    andl $-16, %esp
    subl $16, %esp
    movl $1, 4(%esp)
    movl $.LC0, (%esp)
    call printf
    movl $1, 4(%esp)
    movl $.LC0, (%esp)
    call printf
    xorl %eax, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE11:
    .size    main, .-main
    .ident   "GCC: (Debian 4.7.2-5) 4.7.2"
    .section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

使用-std = c99(这会得到结果1/1):

    .file    "a.c"
    .section .rodata
.LC1:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    movl    $9, 28(%esp)
    movl    $10, 24(%esp)
    fildl   28(%esp)
    fildl   24(%esp)
    fdivrp  %st, %st(1)
    movl    $1, %edx
    fldt    .LC0
    fucomp  %st(1)
    fnstsw  %ax
    sahf
    jp      .L5
    fldt    .LC0
    fucompp
    fnstsw  %ax
    sahf
    je      .L2
    jmp     .L3
.L5:
    fstp    %st(0)
.L3:
    movl    $0, %edx
.L2:
    movzbl  %dl, %eax
    movl    %eax, 4(%esp)
    movl    $.LC1, (%esp)
    call    printf
    movl    $1, 4(%esp)
    movl    $.LC1, (%esp)
    call    printf
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size main, .-main
    .section .rodata
    .align 16
.LC0:
    .long    1717986918
    .long    -429496730
    .long    16382
    .ident   "GCC: (Debian 4.7.2-5) 4.7.2"
    .section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

chu*_*ica 3

在 C 中,浮点数学允许以比代码指示的更高的精度运行。
\n特别是编译时数学(第二行)可以作为long double

\n\n
\n

C11dr \xc2\xa75.2.4.2.2 9 “除了赋值和转换(删除所有额外的范围和精度)之外,具有浮动操作数的运算符和经过通常算术转换的值以及浮动常量所产生的值被评估为其范围和精度可能大于类型所需的格式。

\n
\n\n

请参阅上面的@Patricia Shanahan。

\n\n
\n\n

[编辑]

\n\n

检查 FP 评估模式(如果已定义)

\n\n
#include <float.h>\nprintf("%d\\n", FLT_EVAL_METHOD);\n
Run Code Online (Sandbox Code Playgroud)\n\n

C11dr \xc2\xa75.2.4.2.2 9(续) 评估格式的使用以 的实现定义值为特征FLT_EVAL_METHOD

\n\n

-1无法确定的;

\n\n

0仅根据类型的范围和精度评估所有操作和常量;

\n\n

1将 float 和 double 类型的运算和常量计算为 double 类型的范围和精度,将 long double 运算和常量计算为 long double 类型的范围和精度;

\n\n

2将所有运算和常量评估为 long double 类型的范围和精度。

\n\n

FLT_EVAL_METHOD 的所有其他负值都表示实现定义的行为。

\n

  • @Pascal Cuoq:对,-std=c99 已经做到了。根据文档,它的作用是打开 -fexcess- precision=standard:“编译 C 时,如果指定了 '-fexcess- precision=standard',则超出精度遵循 ISO C99 中指定的规则;特别是,强制转换和赋值都会导致值四舍五入到其语义类型”(PDF 中的第 128 页 (http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc.pdf),而不是_仅赋值_与 -ffloat-store)。 (2认同)