【发布时间】:2018-06-01 06:22:14
【问题描述】:
我正在寻找有关 GCC/x86 中 long double 和 __float128 的详细信息(更多的是出于好奇而不是因为实际问题)。
可能很少有人会需要这些(我只是,有史以来第一次,真正需要double),但我想知道它仍然值得(而且很有趣)你的工具箱里有什么以及它是关于什么的。
鉴于此,请原谅我有些开放的问题:
- 能否解释一下这些类型的实现原理和预期用途,以及相互比较?例如,它们是“尴尬的实现”,因为标准允许该类型,如果它们的精度仅与
double相同,或者它们是否打算作为一流类型,有人可能会抱怨? - 或者,是否有人可以分享一个好的、可用的网络参考?在
"long double" site:gcc.gnu.org/onlinedocs上的 Google 搜索并没有给我带来太多真正有用的信息。 - 假设常见的口头禅“如果你认为你需要双精度,你可能不了解浮点数” 不适用,即你真的需要更高的精度不仅仅是
float,而且一个人不在乎是8字节还是16字节的内存被烧毁了......是否可以合理地期望一个人也可以直接跳转到long double或__float128而不是double显着的性能影响? - Intel CPU 的“扩展精度”特性历来是在内存和寄存器之间移动值时令人讨厌的意外的来源。如果实际存储了 96 位,
long double类型应该可以消除这个问题。另一方面,我知道long double类型与-mfpmath=sse是互斥的,因为在SSE 中没有“扩展精度”之类的东西。另一方面,__float128应该与 SSE 数学完美配合(尽管在没有四精度指令的情况下肯定不是在 1:1 指令库上)。我的这些假设是否正确?
(3. 和 4. 可能可以通过在分析和反汇编上花费一些工作来解决,但也许其他人之前也有同样的想法并且已经完成了这项工作。)
背景(这是 TL;DR 部分):
我最初偶然发现了long double,因为我在<float.h> 中查找DBL_MAX,顺便说一下LDBL_MAX 在下一行。 “哦,看,GCC 实际上有 128 位双打,不是我需要它们,但是……很酷”是我的第一个想法。惊喜,惊喜:sizeof(long double) 返回 12...等等,你是说 16?
不出所料,C 和 C++ 标准没有给出非常具体的类型定义。 C99 (6.2.5 10) 表示 double 的数字是 long double 的子集,而 C++03 声明 (3.9.1 8) 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 位数字,因为 0xdeadbeef 没有被触及,这与 sizeof 返回 12 一致。
__int128 的输出看起来实际上只是一个 128 位数字。所有位最终都会翻转。
使用-m128bit-long-double 编译不 将long double 与 128 位对齐并使用 32 位零填充,如文档所示。它也不使用__int128,但确实似乎与128位对齐,填充值0x7ffdd000(?!)。
此外,LDBL_MAX 似乎对于long double 和__float128 都可以作为+inf 工作。将1.0E100 或1.0E2000 之类的数字与LDBL_MAX 相加或相减会产生相同的位模式。
到目前为止,我认为 foo_MAX 常量将保存最大的可表示数字,not +inf(显然不是这样?)。我也不太确定一个 80 位数字如何可以想象为 128 位值的+inf...也许我在一天结束时太累了,做错了什么。
【问题讨论】:
-
80位双精度可以存储uint64_t。它有 64 位尾数(无可选/隐式前导位)、15 位指数和一个符号位。 en.wikipedia.org/wiki/…
-
不是在
LDBL_MAX中进行加减运算,而是尝试除以二? -
我没有观察到你所看到的。只有 20 个 hex 数字变化,对应扩展精度类型的 10 个字节。一个 80 位数字有 20 个十六进制数字,而不是 18
标签: gcc long-double