我正在寻找有关GCC/x86 的详细信息(更多的是出于好奇而不是因为实际问题)long double
。__float128
可能很少有人会需要这些(我只是,有史以来第一次,真正需要 a double
),但我想知道你的工具箱里有什么以及它是关于什么的仍然是值得的(而且很有趣)。
鉴于此,请原谅我有些开放的问题:
- 有人可以解释这些类型的实现原理和预期用途,也可以相互比较吗?例如,它们是否因为标准允许类型而“令人尴尬的实现”,如果它们只是与 相同的精度
double
,或者它们打算作为一流类型,有人可能会抱怨? - 或者,有人可以分享一个好的、可用的网络参考吗?谷歌搜索
"long double" site:gcc.gnu.org/onlinedocs
并没有给我太多真正有用的东西。 - 假设常见的口头禅“如果你认为你需要双精度,你可能不了解浮点数”不适用,即你真的需要比精度更高的精度
float
,而且不关心是 8 字节还是 16 字节的内存烧毁...是否可以合理地期望一个人也可以直接跳到long double
或者__float128
没有double
显着的性能影响? - 历史上,当值在内存和寄存器之间移动时,英特尔 CPU 的“扩展精度”特性一直是令人讨厌的意外之源。如果实际存储了 96 位,则该
long double
类型应消除此问题。另一方面,我知道该long double
类型与 是互斥的-mfpmath=sse
,因为 SSE 中没有“扩展精度”之类的东西。__float128
,另一方面,在 SSE 数学上应该可以很好地工作(尽管在没有四精度指令的情况下肯定不是在 1:1 指令库上)。我的这些假设是对的吗?
(3. 和 4. 可能可以通过在分析和反汇编上花费一些工作来弄清楚,但也许其他人之前也有同样的想法并且已经完成了这项工作。)
背景(这是 TL;DR 部分):
我最初偶然发现long double
是因为我正在查找DBL_MAX
,<float.h>
顺便说LDBL_MAX
一下在下一行。“哦,看,GCC 实际上有 128 位双打,不是我需要它们,但是……很酷”是我的第一个想法。惊喜,惊喜:sizeof(long double)
返回 12……等等,你是说 16?
毫不奇怪,C 和 C++ 标准没有给出非常具体的类型定义。C99 (6.2.5 10) 表示的数字是C++03 状态 (3.9.1 8)double
的子集,其精度至少与(这是同一件事,只是措辞不同)。基本上,标准将所有内容留给实现,与、和.long double
long double
double
long
int
short
维基百科说 GCC 使用“x86 处理器上的 80 位扩展精度,而不管使用的物理存储如何”。
GCC 文档在同一页面上声明,由于 i386 ABI,类型的大小为 96 位,但任何选项(嗯?什么?)启用的精度不超过 80 位,还有 Pentium 和更新版本处理器希望它们对齐为 128 位数字。这是 64 位下的默认设置,可以在 32 位下手动启用,从而产生 32 位的零填充。
运行测试的时间:
#include <stdio.h>
#include <cfloat>
int main()
{
#ifdef USE_FLOAT128
typedef __float128 long_double_t;
#else
typedef long double long_double_t;
#endif
long_double_t ld;
int* i = (int*) &ld;
i[0] = i[1] = i[2] = i[3] = 0xdeadbeef;
for(ld = 0.0000000000000001; ld < LDBL_MAX; ld *= 1.0000001)
printf("%08x-%08x-%08x-%08x\r", i[0], i[1], i[2], i[3]);
return 0;
}
使用 时long double
,输出看起来有点像这样,标记的数字是恒定的,而所有其他数字最终都会随着数字越来越大而变化:
5636666b-c03ef3e0-00223fd8-deadbeef
^^ ^^^^^^^^
这表明它不是80 位数字。一个 80 位数字有 18 个十六进制数字。我看到 22 个十六进制数字发生了变化,这看起来更像是一个 96 位数字(24 个十六进制数字)。它也不是一个 128 位的数字,因为没有被触及,这与返回 120xdeadbeef
是一致的。sizeof
的输出__int128
看起来真的只是一个 128 位的数字。所有位最终都会翻转。
如文档所示,使用 32 位零填充与 128 位不-m128bit-long-double
对齐。它也不使用,但确实似乎与 128 位对齐,并用值(?!) 填充。long double
__int128
0x7ffdd000
此外,LDBL_MAX
, 似乎对+inf
和 都long double
有效__float128
。在相同的位模式中添加或减去类似1.0E100
或1.0E2000
到/从的数字。
到现在为止,我相信常量将包含最大的可表示数字,但事实并非如此(显然情况并非如此?)。我也不太确定一个 80 位数字如何可以想象128 位值......也许我在一天结束时太累了并且做错了什么。LDBL_MAX
foo_MAX
+inf
+inf