长双精度的性能影响。为什么 C 默认选择 64 位而不是硬件的 80 位?



关于细节,我谈论的是x87 PC体系结构和C编译器。

我正在编写自己的解释器,double数据类型背后的推理让我很困惑,尤其是在效率方面。有人能解释一下为什么C决定使用64位double而不是硬件原生的80位double吗?为什么硬件选择了80位的double,因为它没有对齐?每种方法的性能含义是什么我想使用80位double作为我的默认数字类型但是编译器开发人员的选择让我担心这不是最好的选择。

  1. x86上的double只短了2个字节,为什么编译器默认不使用10个字节的long double
  2. 我能举一个80位long doubledouble相比获得额外精度的例子吗
  3. 为什么Microsoft默认禁用long double
  4. 就幅度而言,long double在典型的x86/x64 PC硬件上有多差/慢

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

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

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

sizeof(float)<=sizeof(双)<=尺寸(长双)

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

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

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

由于更精确,指数范围更大,您可以在没有上溢或下溢的情况下计算中间结果,并且结果的舍入误差更小。

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

这实际上是一个问题中的许多问题,其中一些问题甚至过于宽泛

有人能解释为什么C决定使用64位双精度,而不是硬件原生的80位双精度吗?

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


为什么硬件设置为80位双精度,因为它没有对齐?每种方法的性能含义是什么?

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

对于现代硬件实现者和软件编写者来说,这是一个很好的折衷方案,他们需要在double操作中获得更高的精确舍入精度。太大的类型,你将需要更多的晶体管。将有效位的位数增加一倍,乘法器需要是的4倍

William Kahan,x87算法和最初IEEE 754标准提案的主要设计者,对x87浮点的开发进行了说明:;包含了一个我们敢于使用的扩展格式(80位),以提供与惠普10位十进制计算器中的13位十进制内部格式相同的支持作用"此外,Kahan指出,64位是最宽的有效位,可以在不增加8087的循环时间的情况下进行进位传播,并且x87扩展精度被设计为在未来的处理器中可扩展到更高的精度:;目前,10字节的扩展格式是在超精确算术的价值和实现快速运行的代价之间可以容忍的折衷;很快,两个字节的精度将变得可以忍受,最终16字节的格式。。。在制定IEEE标准754《浮点运算》时,就已经考虑到了这种向更高精度的逐步演变。

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求和这样的特殊技术。它不是默认的浮点类型。事实上,很多人在使用long doublefloat时都会错误地使用浮点文字。他们忘记添加正确的后缀,这导致精度不足,然后他们问为什么long doubledouble完全相同。总之,double应该用于几乎所有情况,除非您受到带宽或精度的限制,并且您真正知道自己在做什么


我能得到一个80位长的双精度与双精度的额外精度的例子吗?

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

  • 80位扩展精度数据类型的应用/优点是什么
  • C和C中长二重和二重的区别++
  • 我想知道长双人间和双人间的区别

为什么Microsoft默认禁用长双精度?

Microsoft默认情况下不会禁用长双精度。他们只是选择将CCD_ 21映射到IEEE-754双精度,顺便说一句,这与CCD_ 22的格式相同。long double型仍然可以正常使用。他们这样做是因为SSE的数学运算更快、更一致。这样你就可以避免";bug";像下面的

  • 为什么将double强制转换为int可能会得到不同的结果
  • 明显相同的数学表达式,但输出不同
  • 为什么在32位和64位机器上,相同的代码会产生不同的数字结果
  • std::pow在32位和64位应用程序中产生不同的结果

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

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

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


就幅度而言,在典型的x86/x64 PC硬件上,长加倍的情况会更糟/更慢多少?

这是无法回答的,因为延迟和吞吐量取决于每个特定的微体系结构。但是,如果您执行大量浮点运算,那么double将明显更快,因为它的有效位更少,并且可以使用SIMD进行并行处理。例如,您可以使用AVX-512处理一次8倍的矢量。扩展精度型无法做到这一点

而且,80位x87fp加载和存储指令明显慢于"0";正常的";转换为32位或64位的版本,并且只有fstp可用,而不是fst请参阅Peter Cordes关于现代CPU上x87性能的回溯计算的回答。(事实上,这是一个跨站点的重复,问MSVC为什么不将80位x87类型公开为long double。)

最新更新