首页 > 解决方案 > PPC64 long double的机器epsilon计算

问题描述

我正在玩在 qemu 中模拟的 PPC64 虚拟机,试图模仿 POWER8 CPU。

在这里,该long double类型与 x86 中用于长双精度的 80 位浮点数不同,并且据我所知,它也不符合 IEEE754 的 float128,因为根据 C 宏它有一个 106 位的尾数LDBL_MANT_DIG(对比。 IEEE754 为其 float128 规定的 112 位尾数)。

维基百科说 IEEE754 float128 的机器 epsilon 应该在 1.93e-34 左右,比 80 位 x86 浮点数 (1.08e-19) 要好得多。

然而,当我尝试在这个虚拟机中获取机器 epsilon 时,我得到了一个相当令人惊讶的答案:

#include <iostream>
int main()
{
    long double eps = 1.0l;
    while (1.0l + 0.5l * eps != 1.0l)
        eps = 0.5l * eps;
    std::cout << eps << std::endl;
    return 0;
}

它输出以下内容:

4.94066e-324

我从LDBL_EPSILON和 从得到相同的结果std::numeric_limits<long double>::epsilon()

这将使它比预期的精确度高出大约 10 倍,逻辑告诉我这应该是不可能的。看到尾数正好是 2x53(IEEE754 的 float64 的),我认为它可能使用双双结构,维基百科还说,它对小数字的精度应该低于 IEEE754 float128。

这里发生了什么?

标签: floating-pointieee-754powerpcepsilondouble-double-arithmetic

解决方案


首先,让我假设您的操作系统是 Linux。到目前为止,64 位 PowerPC 上的所有编译器都默认使用“双双”类型long double,其格式不符合 IEEE 标准。

格式其实是两个doubles的组合,所以理解为struct { double high; double low; }。高的部分与普通的没有什么不同double,而低的部分提供了扩展的尾数。整体的指数与 相同double,这意味着它的最大可表示数不比double's 大几个数量级(因为 double-double 的尾数更长,所以它们仍然不同)。

目前双双浮点数的运算没有原生 PowerPC 指令支持。a+blong double 最终将被转换为对 function 的调用__gcc_qadd。(LLVM 的compiler-rt和 GCC 的libgcc都有自己的实现,见add 函数的源代码

POWER ISA 3.0 (Power9) 开始,支持“binary128”(符合 IEEE 的 128 位浮点类型)的本机指令支持。您可以使用-mcpu=power9 -mfloat128来启用该功能并使用__float128来表示它,或者添加-mabi=ieeelongdouble以使编译器将 long double 视为 binary128 而不是 double-double(以及 C 库声明)。

Binary128 不是两个双精度的组合,而是通过向量寄存器存储/传递,并且比双精度具有更好的精度,尾数为 112 位,指数为 15 位。通过使用 compiler-rt/libgcc 中的支持函数,GCC/Clang 实际上支持带有 VSX(Power7 或更高版本)的目标的 binary128。(例如,为 Power9 和更高版本、为 Power7 和 Power8a+b生成指令)xsaddqp__addkf3

如果您的工具链相对较新(例如,高级工具链-mabi=ieeelongdouble14 或更新版本),请尝试使用 C 代码启用。在 C++ 库支持完成后,GCC 和 Clang计划将来在 64 位 little endian 上将默认的 long double 类型切换为 binary128。


推荐阅读