long double
GCC/x86 に関する詳細な情報を探しています__float128
(実際の問題というよりは好奇心からです)。
おそらくこれらを必要とする人はほとんどいないでしょう (私は初めて本当にを必要としましたdouble
)。
その観点から、私のややオープンな質問を許してください:
- これらのタイプの実装の根拠と意図された使用法を、相互に比較して誰かが説明できますか? たとえば、標準で型が許可されているため、それらは「恥ずかしい実装」であり、 とまったく同じ精度である
double
場合、またはファーストクラスの型として意図されている場合に誰かが文句を言う可能性がありますか? - または、誰かが共有するための優れた使用可能な Web リファレンスを持っていますか? Google で検索し
"long double" site:gcc.gnu.org/onlinedocs
ても、本当に役立つ情報はあまり得られませんでした。 - 「倍精度が必要だと思うなら、おそらく浮動小数点を理解していない」という共通のマントラが当てはまらないと仮定します。燃え尽きました...パフォーマンスに大きな影響を与えることなく、ジャンプしたり、代わりにジャンプしたりできると期待するのは合理的ですか?
float
long double
__float128
double
- Intel CPU の「拡張精度」機能は、歴史的にメモリとレジスタ間で値が移動されたときに厄介な驚きの原因となってきました。実際に 96 ビットが格納されている場合、
long double
型はこの問題を解消するはずです。一方、SSEには「拡張精度」などがないため、long double
タイプが と相互に排他的であることは理解しています。一方、SSE 数学では完全に正常に動作するはずです (ただし、4 倍精度命令がない場合は、1:1 命令ベースではありません)。私はこれらの仮定で正しいですか?-mfpmath=sse
__float128
(3. と 4. は、プロファイリングと逆アセンブルに費やされたいくつかの作業でおそらく把握できますが、他の誰かが以前に同じ考えを持っていて、すでにその作業を行っている可能性があります。)
背景 (これは TL;DR の部分です):で調べていたので
最初につまずきました。偶然にも次の行にいます。「ああ、GCC には実際には 128 ビットの double があります。私がそれらを必要としているわけではありませんが、...クールだ」というのが私の最初の考えでした。驚き、驚き: 12 を返します... 待って、16 のことですか?long double
DBL_MAX
<float.h>
LDBL_MAX
sizeof(long double)
当然のことながら、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 桁の 16 進数があります。22 桁の 16 進数が変更されていることがわかります。これは、96 ビットの数値 (24 桁の 16 進数) のように見えます。また、触れられていないため、128ビットの数値ではありません。これは、 12を返す0xdeadbeef
ことと一致しています。sizeof
の出力は、__int128
実際には 128 ビットの数値のように見えます。すべてのビットが最終的に反転します。
ドキュメントで示されているように、コンパイルは 32 ビットのゼロ パディングで 128 ビットに揃えられ-m128bit-long-double
ません。long double
どちらも使用していません__int128
が、実際には 128 ビットに合わせて、値をパディング0x7ffdd000
(?!) しているようです。
さらに、は との両方でLDBL_MAX
機能するようです。likeまたはto/fromの数値を加算または減算すると、同じビット パターンが生成されます。
これまで、定数は表現可能な最大数を保持するものではないというのが私の信念でした (明らかにそうではありませんか?)。また、80 ビットの数値が 128 ビットの値と同じように機能する可能性があるかどうかもよくわかりません。1 日の終わりに疲れすぎて、何か間違ったことをしただけかもしれません。+inf
long double
__float128
1.0E100
1.0E2000
LDBL_MAX
foo_MAX
+inf
+inf