用于增强中间浮点计算精度的编译器标志

zel*_*ell 5 c c++ floating-point precision compilation

gcc/clang中是否有一个标志指定中间浮点计算的精度?

假设我有一个C代码

double x = 3.1415926;
double y = 1.414;
double z = x * y;
Run Code Online (Sandbox Code Playgroud)

是否有编译器标志允许以用户机器的最高精度计算'x*y',例如,long-double(64位尾数),然后截断为double(53位尾数,声明变量类型的精度)?

仅供参考,我在64位计算机上使用Ubuntu 14.04.

Bas*_*Zen 3

海湾合作委员会

[编辑 gcc 4.8.4 观察到的行为,其中默认行为与文档相反]

您需要使用 x87 FPU 中的 80 位寄存器。通过 with,-mfpmath=387您可以覆盖 SSE 寄存器 XMM0-XMM7 的默认使用。这个默认值实际上为您提供了 IEEE 行为,其中每一步都使用 64 位寄存器。

请参阅: https: //gcc.gnu.org/wiki/x87note

因此,默认情况下 x87 算术不是真正的 64/32 位 IEEE,而是从 x87 单元获得扩展精度。然而,每当将值从寄存器移动到 IEEE 64 或 32 位存储位置时,这个 80 位值必须向下舍入到适当的位数。

然而,如果你的操作极其复杂,则可能会发生寄存器溢出;FP 寄存器堆栈的深度仅为 8。因此,当溢出复制到字大小的 RAM 位置时,您将得到舍入。您需要long double自己声明这种情况并在最后手动舍入,或者检查汇编器输出是否存在显式溢出。

有关寄存器的更多信息,请访问: https://software.intel.com/en-us/articles/introduction-to-x64-assemble

特别是,XMM0...7 寄存器虽然为 128 位宽,但只能容纳两个同时进行的 64 位 FP 操作。因此,您希望看到具有 FLD(加载)、FMUL(乘法)和 FSTP(存储和弹出)指令的堆栈操作 FPR 寄存器。

所以我编译了这段代码:

double mult(double x, double y) {
    return x * y;
}
Run Code Online (Sandbox Code Playgroud)

和:

gcc -mfpmath=387 -Ofast -o precision.s -S precision.c
Run Code Online (Sandbox Code Playgroud)

并得到:

mult:
  .LFB24:
    .cfi_startproc
    movsd   %xmm1, -8(%rsp)
    fldl    -8(%rsp)
    movsd   %xmm0, -8(%rsp)
    fldl    -8(%rsp)
    fmulp   %st, %st(1)
    fstpl   -8(%rsp)
    movsd   -8(%rsp), %xmm0
    ret
    .cfi_endproc
Run Code Online (Sandbox Code Playgroud)

现在一切都变得非常有意义了。浮点值通过寄存器 XMM0 和 XMM1 传递(尽管它们必须在内存中进行奇怪的往返才能放入 FPR 堆栈),并且根据上述 Intel 参考,结果在 XMM0 中返回。不知道为什么没有直接来自 XMM0/1 的简单 FLD 指令,但显然指令集不会这样做。

如果与 相比-mfpmath=sse,后一种情况需要做的事情要少得多,因为操作数已准备好并在 XMM0/1 寄存器中等待,并且就像单个 MULSD 指令一样简单。