C ++处理过多的精度

时间:2014-01-01 14:58:10

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

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

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

  

' - 对于C

以外的语言没有实现fexcess-precision = standard'

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

我想对于我目前的用例,我可能会在任何情况下都使用-mfpmath=sse,据我所知,这不应该产生任何过高的精度。但我仍然很好奇。

2 个答案:

答案 0 :(得分:4)

  

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

C99标准定义了FLT_EVAL_METHOD,这是一个编译器集宏,用于定义在C程序中应该发生多少精度(许多C编译器的行为方式仍然不能完全符合最合理的解释。它们定义的FP_EVAL_METHOD的值:生成387代码的旧GCC版本,生成387代码时的Clang,...)。在C11标准中阐明了与FLT_EVAL_METHOD的影响有关的微妙点。

自2011年标准以来,C ++ defers to C99用于定义FLT_EVAL_METHOD(标题cfloat)。

所以GCC应该只允许-fexcess-precision=standard用于C ++,并且希望它最终能够。与C语义相同的语义已经在C ++标准中,它们只需要在C ++编译器中实现。


  

我想对于我目前的用例,我可能会在任何情况下使用-mfpmath = sse,据我所知,这不应该产生任何过高的精度。

这是通常的解决方案。

请注意,C99还在math.h中定义了FP_CONTRACT,您可能需要查看它:它涉及一些表达式以更高精度计算的相同问题,从完全不同的方面打击(现代的fusion-multiply-add指令代替旧的387指令集。这是一个pragma来决定是否允许编译器用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");

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

答案 1 :(得分: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的错误,如果没有标记,我会得到与-fexcess-precision=fast给予C相同的行为。添加-std=c++11没有帮助。所以现在帕斯卡已经表达的怀疑是官方的:g ++并不一定在它应该的任何地方四舍五入。