【问题标题】:Floating point number comparison via integer casting in C language通过 C 语言中的整数转换进行浮点数比较
【发布时间】:2014-03-28 14:35:39
【问题描述】:

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

#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;
}

我认为这可能取决于编译器,我的是 gcc (GCC mingw Windows7) 4.8.1 和 gcc (Debian 4.7.2-5) 4.7.2。

非常感谢!

更新!

我生成了带有和不带有 -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

使用 -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

【问题讨论】:

  • 这不可能是真的...您能否将ab 打印为整数并检查它们是否为9 和10? This website 也声称使用相同的编译器,它会为两者打印1,就像它应该的那样。
  • 为 gcc 4.8.2 打印 1/1
  • 由于浮点数学不精确(并且编译器以不同的精度存储临时结果),我希望这样的行为。但我在 gcc (x64) 上也得到 1/1。
  • 您可以通过直接打印两个除法结果以高精度获得更多信息,而不是通过比较它们。我的猜测是,对于您的编译器,带有文字的编译器在编译时进行评估,带有变量的编译器在运行时进行评估,结果具有不同的精度。
  • @ThoAppelsin:我花了半天时间才发现这对我的程序造成了问题。我不相信它,但它是真的。 4.8.1 编译器的更多信息:它来自 Windows7 上的 MinGW(我不知道是否重要)。

标签: c gcc floating-point comparison


【解决方案1】:

在 C 语言中,允许以比代码指示的更高的精度运行浮点数学运算。
尤其是编译时数学(第 2 行)可能以 long double

的形式运行

C11dr §5.2.4.2.2 9 “除了赋值和强制转换(删除所有额外的范围和精度),具有浮动操作数的运算符产生的值和经过通常算术转换的值和浮动常量被评估为范围和精度可能大于类型要求的格式。

参见上面的@Patricia Shanahan。


[编辑]

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

#include <float.h>
printf("%d\n", FLT_EVAL_METHOD);

C11dr §5.2.4.2.2 9(续)评估格式的使用以FLT_EVAL_METHOD 的实现定义值为特征。

-1 无法确定;

0 仅根据 类型;

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

2 评估所有操作和常量的范围和精度 long double 类型。

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

【讨论】:

  • 不错。 -std=c11 启用宏(即 2)并打印 1/1。 g++ -std=c++11 仍然打印 0/1,并且宏未定义。
  • GCC 实现了 C99(参见 gcc.gnu.org/ml/gcc-patches/2008-11/msg00105.html ),但遗憾的是,除非程序员明确要求,否则不会激活它。使用-std=c99-std=c11 在GCC 中激活符合C99 的FLT_EVAL_METHOD 语义。
  • @Peter Schneider 谢谢。我怀疑也有可能有c11 和各种FLT_EVAL_METHOD 设置。我想知道 OP 的 FLT_EVAL_METHOD 设置是什么?
  • @Pascal Cuoq:是的,-std=c99 已经做到了。根据文档,它的作用是打开 -fexcess-precision=standard:“在编译 C 时,如果指定了 '-fexcess-precision=standard',则超额精度遵循 ISO C99 中指定的规则;特别是,强制转换和赋值都会导致值被四舍五入到它们的语义类型”(PDF 中的第 128 页 (gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc.pdf),而不是 assignments only with -ffloat-store)。
猜你喜欢
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
  • 2017-05-30
  • 1970-01-01
  • 2015-01-20
  • 1970-01-01
  • 2011-10-23
  • 1970-01-01
相关资源
最近更新 更多