带有和没有-ansi差异的gcc浮点

时间:2016-06-09 16:33:46

标签: gcc floating-point ansi

考虑两个程序。第一个在gcc 5.3.0上打印“Unequal”(目标:i686-pc-cygwin)。使用-ansi选项时会打印“Equal”。

int main () {
        double d = 2.335 - 2.334;
        double q = 0.001;
        if (d == q) {
            printf ("Equal\n");
        } else {
            printf ("Unequal\n");
        }
    return 0;
 }

第二个使用或不使用-ansi选项打印“Unequal”。

int main () {
    if (2.335 - 2.334 == 0.001) {
        printf ("Equal\n");
    } else {
        printf ("Unequal\n");
    }
    return 0;
}

差异的根源是什么? 当然,众所周知,不应对实数进行相等性测试。我理解IEEE754标准对涉及浮点的计算(im-)精度的影响。但是,据我所知,这两个程序在语义上应该是等效的,并给出相同的结果。

在C99模式的第一个中是否有一些隐式转换在C99中删除了?

2 个答案:

答案 0 :(得分:2)

C99和C11精确定义了当主机平台只能方便地计算到比floatdouble更高的精度时会发生什么。早期的C89(或“ANSI”)C标准没有。在C99或C11中,编译器将FLT_EVAL_METHOD定义为1或2,它告诉程序员浮点常量和操作将被解释为比其类型更高的精度。

这是在this message中讨论的补丁中的GCC中实现的。 默认情况下,补丁提供的选项-fexcess-precision=standard在C99和C11中启用,但在“ANSI”(C89)模式下未启用。

尝试解释编译器在C89模式下的作用并没有多大意义:它有点模糊,浮点变量的值在没有赋值的情况下发生变化,或者在优化级别之间变化,如this report中所述。在C99模式下,编译器将FLT_EVAL_METHOD定义为2,编译器将差值2.335 - 2.334计算为80位浮点数,即80位之间的差值FP表示2335/1000和80位FP表示2334/1000。这个数字恰好与1/1000的80位表示不同。这就是为什么测试程序的第二个版本与没有-ansi的情况一样。在测试程序的第一个版本中,对double变量的赋值会导致数字四舍五入为双精度(64位)浮点值。因此,它们在被舍入后都是相等的。

答案 1 :(得分:0)

警告:这可能不是一个完整的答案,为什么,但这里的一些数据基于编译各种选项和反汇编输出......

第二个程序[仅使用文字常量] 生成任何浮点指令。它只做一个printf。 (即所有计算都在编译器内完成。)

因此,以下仅限于第一个程序。

您没有指定您为gcc提供的[其他]命令行选项,但我只想到-ansi

我有一台64位的linux机器[gcc 5.3.1],所以我不得不添加-m32

没有它,反汇编[64位],有或没有-ansi相同并产生Unequal [它使用XMM指令] 。使用-O2,生成浮点指令,只需printf [有或没有-ansi]。同样,也是如此。

如果我使用-m32-O2,则只会生成printf且输出为Unequal,无论-ansi是否

唯一的差异发生在32位,优化关闭和使用-ansi之间。在这里,gcc生成旧/传统387 FP协处理器单元的指令。

没有-ansi,这里是反汇编:

 dyn.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 14                sub    $0x14,%esp
  11:   dd 05 10 00 00 00       fldl   0x10
  17:   dd 5d f0                fstpl  -0x10(%ebp)
  1a:   dd 05 18 00 00 00       fldl   0x18
  20:   dd 5d e8                fstpl  -0x18(%ebp)
  23:   dd 45 f0                fldl   -0x10(%ebp)
  26:   dd 45 e8                fldl   -0x18(%ebp)
  29:   df e9                   fucomip %st(1),%st
  2b:   dd d8                   fstp   %st(0)
  2d:   7a 1e                   jp     4d <L00>
  2f:   dd 45 f0                fldl   -0x10(%ebp)
  32:   dd 45 e8                fldl   -0x18(%ebp)
  35:   df e9                   fucomip %st(1),%st
  37:   dd d8                   fstp   %st(0)
  39:   75 12                   jne    4d <L00>
  3b:   83 ec 0c                sub    $0xc,%esp
  3e:   68 00 00 00 00          push   $0x0
  43:   e8 fc ff ff ff          call   44 <main+0x44>
  48:   83 c4 10                add    $0x10,%esp
  4b:   eb 10                   jmp    5d <L01>
  4d:L00 83 ec 0c               sub    $0xc,%esp
  50:   68 06 00 00 00          push   $0x6
  55:   e8 fc ff ff ff          call   56 <main+0x56>
  5a:   83 c4 10                add    $0x10,%esp
  5d:L01 b8 00 00 00 00         mov    $0x0,%eax
  62:   8b 4d fc                mov    -0x4(%ebp),%ecx
  65:   c9                      leave
  66:   8d 61 fc                lea    -0x4(%ecx),%esp
  69:   c3                      ret

对于-ansi,单个指令与相同

--- dynstd.dis  2016-06-09 09:58:18.719906988 -0700
+++ dynansi.dis 2016-06-09 09:58:44.266286688 -0700
@@ -14,7 +14,7 @@
    e:  83 ec 14                sub    $0x14,%esp
   11:  dd 05 10 00 00 00       fldl   0x10
   17:  dd 5d f0                fstpl  -0x10(%ebp)
-  1a:  dd 05 18 00 00 00       fldl   0x18
+  1a:  dd 05 10 00 00 00       fldl   0x10
   20:  dd 5d e8                fstpl  -0x18(%ebp)
   23:  dd 45 f0                fldl   -0x10(%ebp)
   26:  dd 45 e8                fldl   -0x18(%ebp)

请注意,差异略高于fldl 0x10。然后,如果没有-ansi,则后跟fldl 0x18。使用-ansi,后跟fldl 0x10。因此,如果没有-ansi [我最好的猜测],我们正在比较0x10 == 0x18 [不平等]和-ansi我们正在比较0x10 == 0x10 [等于]

几乎作为旁注,我使用clang重复了相同的测试,但即使使用-m32,它也会生成XMM条指令,反汇编是相同的,输出始终是Unequal

所以,AFAICT,对于一组有限的选项,这可能是gcc的代码生成问题(即错误)。