5

具体来说,我说的是 x87 PC 架构和 C 编译器。

我正在编写自己的解释器,double数据类型背后的推理让我感到困惑。尤其是在效率方面。有人可以解释为什么 C 决定使用 64 位double而不是硬件原生 80 位double吗?为什么硬件设置为 80-bit double,因为它没有对齐?每个的性能影响是什么?我想double为我的默认数字类型使用 80 位。但是编译器开发人员的选择让我担心这不是最佳选择。

  1. double在 x86 上只短了 2 个字节,为什么编译器long double默认不使用 10 个字节?
  2. 我可以举一个 80-bit long doublevs获得的额外精度的例子double吗?
  3. 为什么微软long double 默认禁用?
  4. long double就数量级而言,典型的 x86/x64 PC 硬件会差多少/慢多少?
4

3 回答 3

3

根据 Mysticial 的说法,答案是微软使用SSE2作为其double数据类型。与现代 CPU 扩展相比,浮点单元 (FPU) x87 被视为过时且速度较慢。SSE2 不支持 80 位,因此编译器选择 64 位精度。

在 32 位 x86 架构上,由于所有 CPU 还没有 SSE2,微软仍然使用浮点单元 (FPU) x87,除非/arch:SSE2给出编译器开关。这使得代码与旧代码不兼容?CPU。

于 2012-04-21T04:16:57.237 回答
2

这实际上是一个问题,其中一些甚至太宽泛了

有人可以解释为什么 C 决定使用 64 位双精度而不是硬件本机 80 位双精度吗?

它与 C 无关,因为 C 标准只规定了内置类型的最低要求,完全取决于编译器实现来选择他们想要用于类型的任何格式。没有什么能阻止 C 编译器使用一些定制的 77 位浮点类型


为什么硬件设置为 80 位双精度,因为它没有对齐?每个的性能影响是什么?

它与 2 个字节的倍数对齐。请记住,x87 可以追溯到 8086 + 8087。

对于现代硬件实现者和软件编写者来说,这是一个很好的权衡,他们需要更高的精度来进行精确的四舍五入double操作。太大的类型,您将需要更多的晶体管。将有效数中的位数加倍,乘数需要是原来的 4 倍

x87 算术和初始 IEEE 754 标准提案的主要设计者 William Kahan 关于 x87 浮点开发的说明:“我们敢于扩展的格式(80 位)被包括在内,以提供与 13 - 十进制内部格式用于惠普的 10 位十进制计算器。”此外,Kahan 指出 64 位是在不增加 8087 循环时间的情况下进行进位传播的最宽有效位,并且 x87 扩展精度旨在在未来的处理器中扩展到更高的精度:“目前,10 -byte 扩展格式是超精确算术的价值和实现它以快速运行的代价之间的一个可容忍的折衷;很快,两个字节的精度将变得可以容忍,最终是 16 字节格式……那种当 IEEE Standard 754 for Floating-Point Arithmetic 制定时,就已经考虑到逐渐向更广泛的精度发展。

https://en.wikipedia.org/wiki/Extended_precision#IEEE_754_extended_precision_formats

如您所见,使用 64 位有效位,您可以与整数 ALU 共享组件(加法器、乘法器...)。


我想为我的默认数字类型使用 80 位双精度。但是编译器开发人员的选择让我担心这不是最佳选择。x86 上的 double 仅短 2 个字节,为什么编译器默认不使用 10 字节长的 double?

它实际上旨在用作临时变量(如tmp = (b*c + d)/e)以避免内部溢出或下溢问题,而无需像Kahan summation这样的特殊技术。这不是您的默认浮点类型。long double事实上,很多人在使用or时错误地使用了浮点字面量float。他们忘记添加正确的后缀,导致精度不足,然后他们问为什么long doubledouble. 总之,double应该用于几乎所有情况,除非您受到带宽或精度的限制并且您真的知道自己在做什么


我可以举一个 80 位长双精度与双精度的例子吗?

您可以打印完整的值并自己查看。还有很多值得一读的问题


为什么微软默认禁用 long double?

默认情况下, Microsoft不会禁用 long double。他们只是选择映射long double到 IEEE-754 双精度,顺便说一下与double. 该类型long double仍然可以正常使用。他们这样做是因为 SSE 上的数学运算更快、更一致。这样你就可以避免像下面这样的“错误”

除了 64 位 long double 没有奇数大小,这需要编译器多填充 6 个零字节(或处理非 2 的幂类型宽度),这是一种资源浪费。

也就是说,甚至不是说 80 位long double在 x86 上不可用。目前只有 MSVC 放弃了扩展精度类型,其他 x86 编译器(如 GCC、Clang、ICC...)仍然支持它,并将80 位 IEEE-754 设为long double 的默认格式。例如GCC 具有-mlong-double-64/80/128-m96/128bit-long-double控制的确切格式long double

或者在不通过更改可能破坏 ABI 兼容性的情况下long double,您可以使用GNU C 浮点类型名称,例如__float80在支持它的目标上。 Godbolt 上的这个示例无论是针对 Windows 还是 Linux 都编译为 80 位 FP 数学。


就数量级而言,在典型的 x86/x64 PC 硬件上,long double 会差多少/慢多少?

这是无法回答的,因为延迟和吞吐量取决于每个特定的微架构。但是,如果您执行大量浮点运算,则double速度会明显加快,因为它的有效位数较少,并且可以与 SIMD 并行化。例如,您可以使用AVX-512一次处理 8 个双精度数的向量。扩展精度类型无法做到这一点

此外,80 位 x87 fp 加载和存储指令比转换为/从 32 位或 64 位转换的“正常”版本慢得多,并且仅fstp可用,而不是fst. 请参阅Peter Cordes关于在现代 CPU 上进行 x87 性能逆向计算的回答。(事实上​​,这是一个跨站点副本,询问为什么 MSVC 不将 80 位 x87 类型公开为long double.)

于 2013-08-08T00:33:06.157 回答
2

错误的问题。它与 C 无关,所有语言都使用 AFAIK 作为 32 位的标准浮点单精度和 64 位的双精度。C 作为支持不同硬件的语言仅定义

sizeof(float) <= sizeof(double) <= sizeof(long double)

因此,一个特定的 C 编译器对所有数据类型使用 32 位浮点数是完全可以接受的。

英特尔决定 Kahans 建议他们支持尽可能高的精度,并且应在内部以 80 位精度执行精度较低的格式(32 位和 64 位)的计算。

精度和指数范围的差异:64位大约有。16 位十进制数字,最大指数为 308,80 位为 19 位,最大指数为 4932。

更加精确并且具有更大的指数范围,您可以计算没有溢出或下溢的中间结果,并且您的结果具有更少的舍入误差。

所以问题是为什么long double不支持80bit。事实上,许多编译器确实支持它,但缺乏使用和运行基准性能有效地扼杀了它。

于 2012-04-24T13:54:02.260 回答