12

我目前正在查看执行多精度浮点运算的代码。为了正常工作,该代码需要在明确定义的点处将值减少到它们的最终精度。因此,即使将中间结果计算到80 位扩展精度浮点寄存器,在某些时候也必须将其舍入到64 位双精度以进行后续操作。

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

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

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

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

4

2 回答 2

8

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

C99 标准定义FLT_EVAL_METHOD了一个编译器集宏,它定义了如何在 C 程序中发生超额精度(许多 C 编译器的行为方式仍然不完全符合对FP_EVAL_METHOD它们定义的值的最合理解释:旧 GCC生成 387 代码的版本,生成 387 代码时的 Clang,...)。FLT_EVAL_METHODC11 标准中阐明了与影响有关的细微之处。

自 2011 年标准以来,C++遵循C99来定义FLT_EVAL_METHOD(header cfloat)。

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


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

这是通常的解决方案。

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

此选项的默认值是实现定义的,有些人认为默认值是允许生成 FMA 指令(对于否则定义FLT_EVAL_METHOD为 0 的 C 编译器)。在 C 语言中,您应该通过以下方式确保您的代码不会过时:

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

如果您的编译器记录了一个,那么 C++ 中的等效咒语。


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

恐怕这个问题的答案是 GCC 的行为,比如在生成 387 代码时,是无意义的。请参阅促使 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");

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

于 2014-01-01T15:38:59.793 回答
4

但是在没有任何开关的情况下,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如果没有标志,我会得到与 C 相同的行为-fexcess-precision=fast。添加-std=c++11没有帮助。所以现在帕斯卡已经表达的怀疑是官方的:g ++不一定会在它应该的地方四处走动。

于 2014-01-01T16:42:09.460 回答