【问题标题】:Which registers do x86/x64 processors use for floating point math?x86/x64 处理器使用哪些寄存器进行浮点数学运算?
【发布时间】:2016-05-02 14:36:01
【问题描述】:

x86/x64 是使用 SIMD 寄存器进行高精度浮点运算还是使用专用的 FP 寄存器?

我指的是高精度版本,而不是常规的double 精度。

【问题讨论】:

  • “高精度”全部由软件完成。除了 x87 FPU 中的双精度和扩展精度之外,处理器一无所知。
  • @Mysticial - 我所知道的每个处理器都有分辨率超过 64 位的 FP 寄存器,用于内部操作以最大限度地减少近似误差。
  • 软件无法访问那些“内部”寄存器。它们仅略大于 53/64 位,因此最终结果精确到 53/64 位。在舍入之前,乘法可能高达 106/128 位。但您无法访问它们。
  • 您可以访问指令集公开的寄存器。其中包括 x87 FPU/SSE/AVX。但是没有软件可以使用的更高精度版本。是的,在内部,处理器可能有更高精度的寄存器,但它们仅用于内部操作,这些操作在放回 ISA 可访问寄存器时将全部舍入为 53/64 位。
  • @Mysticial 正确舍入的乘法是用结果宽度的三个附加位实现的,最后一个是特殊连接的(“粘性位”)。在en.wikipedia.org/wiki/Floating_point上向下滚动到“置顶”

标签: floating-point x86 64-bit simd cpu-registers


【解决方案1】:

正如@EricPostpischil 指出的那样,FPU 堆栈仍然可用,并且公开了一个 80 位精度的算法(不确定处理器是否仍然具有完整的逻辑,或者这部分是否在硬件级别进行了仿真)。它以long double 类型提供给GCC 中的开发人员。例如为方法生成的程序集

long double f(long double a, long double b)
{
    return a*b ;
}

    fldt    16(%rbp)
    fldt    32(%rbp)
    fmulp   %st, %st(1)

archive email 提供了使用此类数据的有用元素,例如:

long double my_logl(long double x)
{
  long double y;
  __asm__ volatile(
    "fldln2\n"
    "fldl   %1\n"
    "fyl2x"
    : "=t" (y) : "m" (x));
  return y;
}

在没有 SSE、AVX 或其他向量扩展的情况下编译代码时,您的代码可能会使用 80 位 FPU 生成此类指令,并且可能会输出不同的值。下面是一个示例代码来说明:

double epstest(long double a, long double b)
{
        long double y ;
        y = a + b ;
        y = y - a ;
        return y ;
}

#include <cstdio>

int main()
{
        double x = 1.0 ;
        double y = 1e-17 ;
        double z = x + y ;
        z = z - x ;
        printf ("double: %lf + %le - %lf = %le\n",  x, y, x, z);
        double res = epstest (x, y) ;
        printf ("long double: %lf + %le - %lf = %le\n",  x, y, x, res);
        return 0 ;
}

还有输出:

double: 1.000000 + 1.000000e-17 - 1.000000 = 0.000000e+00
long double: 1.000000 + 1.000000e-17 - 1.000000 = 9.974660e-18

更高的精度(超过long double)在 x86_64 的软件中实现。

【讨论】:

  • x87 在当前一代 x87 CPU 上仍然具有高性能,正如您从 Agner Fog's insn tables 中看到的那样。例如,在 Skylake 上,fadd 是 3c 延迟,每 1c 吞吐量一个(并在端口 5 上运行!)。只有复杂的 x87 insns(如 fsin)是微编码的。即使fsqrt 也很快。不过,加载/存储 80 位浮点数非常慢。
  • 相比之下,在 Skylake 上,addps/addss 是 4c 延迟,每 0.5c 吞吐量一个。 (Skylake 上的 SSE/AVX add/sub/mul/fma 都具有相同的性能,因为它们放弃了在 Broadwell 之前存在的低延迟专用添加单元。)
  • 另外,您不必使用-O0 或类似的蠢东西。 x86-64 SysV ABI 指定 long double 是 80 位 x87 类型,因此使用该类型的代码需要具有如此高的精度。
  • @PeterCordes,我基本上认为 x87 已经过时了。为什么有人会使用 x87,例如使用 Skylake 处理器?
  • @Zboson:这是迄今为止获得 80 位精度浮点数的最快方法。另外,我不记得是哪个 glibc 函数,但我确信我看到一个使用 x87 来做某事,故意使用手写 asm,用于一些简单的 x87 指令(不像 fsin)。在尝试再次找到它时,我确实找到了 glibc 的 difftime 实现,它使用 long double 来获得两个 time_t 整数之间的 double 差异。 time_t 是在 x86-64 上签名的,所以 glibc 实际上在这里很傻。可以精确表示每个 64 位整数的 FP 类型很有趣。 FILD m64int 即使在 32 位中也存在。
【解决方案2】:

FPU(浮点单元)具有用于 80 位浮点值的寄存器(采用 Intel 格式,即 IEEE 754 格式,略有变化)。

各种 SIMD 单元(SSE、AVX 等)具有更大的寄存器,可用于多种用途,但只有将它们用作 32 位和 64 位浮点的指令。

【讨论】:

    猜你喜欢
    • 2014-05-07
    • 2012-03-25
    • 1970-01-01
    • 2015-03-28
    • 2023-03-17
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    相关资源
    最近更新 更多