【问题标题】:C++ handling of excess precision超精度的 C++ 处理
【发布时间】:2014-01-19 03:08:38
【问题描述】:

我目前正在查看code which does multi-precision floating-point arithmetic。为了正常工作,该代码需要在明确定义的点处将值减少到它们的最终精度。因此,即使将中间结果计算到 80 bit extended precision 浮点寄存器,在某些时候它也必须舍入到 64 bit double 以进行后续操作。

代码使用宏INEXACT来描述这个需求,但没有完美的定义。 gcc manual 提到 -fexcess-precision=standard 作为强制转换和赋值操作定义明确精度的一种方式。但是,它也写道:

'-fexcess-precision=standard' 未针对 C 以外的语言实现

现在我正在考虑将这些想法移植到 C++(如果有人知道现有实现,欢迎 cmets)。所以看来我不能将该开关用于 C++。 但是在没有任何开关的情况下,g++ 的默认行为是什么?是否有更多类似 C++ 的方法来控制超精度的处理?

我想对于我当前的用例,无论如何我都可能使用-mfpmath=sse,据我所知,这不会导致任何过度的精度。但我还是很好奇。

【问题讨论】:

    标签: c++ gcc floating-point floating-point-precision extended-precision


    【解决方案1】:

    是否有更多类似 C++ 的方法来控制超精度的处理?

    C99 标准定义了FLT_EVAL_METHOD,这是一个编译器集宏,它定义了在 C 程序中应该如何发生超额精度(许多 C 编译器的行为方式仍然不完全符合对FP_EVAL_METHOD 他们定义:旧的 GCC 版本生成 387 代码,生成 387 代码时的 Clang,......)。在 C11 标准中阐明了与 FLT_EVAL_METHOD 效果相关的细微之处。

    自 2011 年标准起,C++ defers to C99FLT_EVAL_METHOD 的定义(标头 cfloat)。

    所以 GCC 应该简单地允许 C++ 使用 -fexcess-precision=standard,并希望它最终会这样做。与 C 相同的语义已经在 C++ 标准中,它们只需要在 C++ 编译器中实现。


    我想对于我当前的用例,我可能会在任何情况下使用 -mfpmath=sse,据我所知,这不会导致任何过度的精度。

    这是通常的解决方案。

    请注意,C99 还在 math.h 中定义了您可能想要查看的 FP_CONTRACT:它与以更高精度计算某些表达式的相同问题有关,从完全不同的方面(现代融合- 乘加指令而不是旧的 387 指令集)。这是一个编译指示,用于决定是否允许编译器用 FMA 指令替换源代码级加法和乘法(这具有以无限精度虚拟计算乘法的效果,因为这是该指令的工作方式,而不是四舍五入到类型的精度,就像使用单独的乘法和加法指令一样)。这个 pragma 显然没有被纳入 C++ 标准(据我所知)。

    此选项的默认值是实现定义的,有些人认为默认值是允许生成 FMA 指令(对于否则将 FLT_EVAL_METHOD 定义为 0 的 C 编译器)。 在 C 语言中,您应该面向未来 你的代码:

    #include <math.h>
    #pragma STDC FP_CONTRACT off
    

    如果你的编译器记录了一个类似的 C++ 咒语。


    在没有任何开关的情况下,g++ 的默认行为是什么?

    恐怕这个问题的答案是 GCC 的行为,比如生成 387 代码时,是无意义的。请参阅situation 的描述,这促使 Joseph Myers 修复了 C 的情况。如果 g++ 没有实现-fexcess-precision=standard,这可能意味着 80 位计算被随机四舍五入到编译器发生时的类型精度必须将一些浮点寄存器溢出到内存中,导致下面的程序在程序员无法控制的某些情况下打印“foo”:

    if (x == 0.0) return;
    ... // code that does not modify x
    if (x == 0.0) printf("foo\n");
    

    ...因为省略号中的代码导致保存在 80 位浮点寄存器中的 x 溢出到堆栈上的 64 位槽中。

    【讨论】:

    • 这是一个很好的答案,我将不得不阅读它几次才能完全吸收它。
    • @ShafikYaghmour 最后一个例子,实际上他在答案中的大部分想法,已经可以在 arxiv.org/abs/cs/0701192 中找到,它主要是为 C 编写的(当时 C 编译器编译错误浮点)。如今,C 编译器的情况更加清晰,但这种清晰性还没有渗透到 C++ 中。无论如何,如果你觉得这个主题值得 50 页左右,强烈推荐 David 的文章。
    • "if (x == 0.0) return;..." 编译器是否容易在优化期间生成此类代码? (然后打破它)
    【解决方案2】:

    但是在没有任何开关的情况下,g++ 默认行为是什么?

    我自己通过实验找到了一个答案,使用以下代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char** argv) {
      double a = atof("1.2345678");
      double b = a*a;
      printf("%.20e\n", b - 1.52415765279683990130);
      return 0;
    }
    

    如果 b 被四舍五入 (-fexcess-precision=standard),则结果为零。否则 (-fexcess-precision=fast) 类似于8e-17。使用-mfpmath=387 -O3 编译,我可以为gcc-4.8.2 重现这两种情况。对于g++-4.8.2,如果我尝试这样做,我会收到-fexcess-precision=standard 的错误,并且没有标志我会得到与-fexcess-precision=fast 为C 提供的相同行为。添加-std=c++11 没有帮助。所以现在 Pascal 已经表达的怀疑是官方的:g++ 并不一定会在所有它应该的地方四舍五入。

    【讨论】:

      猜你喜欢
      • 2014-05-20
      • 1970-01-01
      • 2018-08-27
      • 2019-05-22
      • 2018-03-13
      • 1970-01-01
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      相关资源
      最近更新 更多