超精度的 C++ 处理

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

I'm currently looking at code which does multi-precision floating-point arithmetic. To work correctly, that code requires values to be reduced to their final precision at well-defined points. So even if an intermediate result was computed to an 80 bit extended precision floating point register, at some point it has to be rounded to 64 bit double for subsequent operations.

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

The code uses a macro INEXACT to describe this requirement, but doesn't have a perfect definition. The gcc manual mentions -fexcess-precision=standard as a way to force well-defined precision for cast and assignment operations. However, it also writes:

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

‘-fexcess-precision=standard’ is not implemented for languages other than C

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

Now I'm thinking about porting those ideas to C++ (comments welcome if anyone knows an existing implementation). So it seems I can't use that switch for C++. But what is the g++ default behavior in absence of any switch? Are there more C++-like ways to control the handling of excess precision?

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

I guess that for my current use case, I'll probably use -mfpmath=sse in any case, which should not incur any excess precision as far as I know. But I'm still curious.

推荐答案

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

Are there more C++-like ways to control the handling of excess precision?

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

The C99 standard defines FLT_EVAL_METHOD, a compiler-set macro that defines how excess precision should happen in a C program (many C compilers still behave in a way that does not exactly conform to the most reasonable interpretation of the value of FP_EVAL_METHOD that they define: older GCC versions generating 387 code, Clang when generating 387 code, …). Subtle points in relation with the effects of FLT_EVAL_METHOD were clarified in the C11 standard.

自 2011 年标准以来,C++ 遵从 C99 的定义FLT_EVAL_METHOD(标题 cfloat).

Since the 2011 standard, C++ defers to C99 for the definition of FLT_EVAL_METHOD (header cfloat).

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

So GCC should simply allow -fexcess-precision=standard for C++, and hopefully it eventually will. The same semantics as that of C are already in the C++ standard, they only need to be implemented in C++ compilers.

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

I guess that for my current use case, I'll probably use -mfpmath=sse in any case, which should not incur any excess precision as far as I know.

这是通常的解决方案.

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

Be aware that C99 also defines FP_CONTRACT in math.h that you may want to look at: it relates to the same problem of some expressions being computed at a higher precision, striking from a completely different side (the modern fused-multiply-add instruction instead of the old 387 instruction set). This is a pragma to decide whether the compiler is allowed to replace source-level additions and multiplications with FMA instructions (this has the effect that the multiplication is virtually computed at infinite precision, because this is how this instruction works, instead of being rounded to the precision of the type as it would be with separate multiplication and addition instructions). This pragma has apparently not been incorporated in the C++ standard (as far as I can see).

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

The default value for this option is implementation-defined and some people argue for the default to be to allow FMA instructions to be generated (for C compilers that otherwise define FLT_EVAL_METHOD as 0). You should, in C, future-proof your code with:

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

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

And the equivalent incantation in C++ if your compiler documents one.

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

what is the g++ default behavior in absence of any switch?

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

I am afraid that the answer to this question is that GCC's behavior, say, when generating 387 code, is nonsensical.?See the description of the situation that motivated Joseph Myers to fix the situation for C. If g++ does not implement -fexcess-precision=standard, it probably means that 80-bit computations are randomly rounded to the precision of the type when the compiler happened to have to spill some floating-point registers to memory, leading the program below to print "foo" in some circumstances outside the programmer's control:

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

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

… because the code in the ellipsis caused x, that was held in an 80-bit floating-point register, to be spilt to a 64-bit slot on the stack.

相关文章