我目前正在关注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
,据我所知,这不应该产生任何过高的精度。但我仍然很好奇。
答案 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 ++并不一定在它应该的任何地方四舍五入。