はじめに
x86系では、long doubleが内部80bit精度の拡張倍精度浮動小数点数フォーマットが採用されている。おそらくx87系命令の内部精度が80bitなのと合わせてあるのだと思われる。それをざっと確認しておく。
拡張倍精度浮動小数点数フォーマットとは
Wikipediaのこの図の通りなんだけれど、要するに
- 符号 1bit
- 指数部 15bit
- 仮数部 64bit
の合計80bitの表現になっている。ここで気をつけなくてはならないのは、この仮数部はいわゆる「ケチ表現」になっておらず、正規化した数字の最上位ビット1を省略しないこと。従って、単精度、倍精度と仮数部のbit数を比較するなら、63bitの精度になっている。
long double
環境に依存するのだろうが、手元のMacやx86 Linux+GCCの組み合わせでは、long double
のサイズは16バイト、精度は80bitの拡張倍精度浮動小数点数フォーマットになっていた。
拡張倍精度浮動小数点数フォーマットを見てみる
というわけでそのビット表現を見てみる。こんなコードを書いてみよう。
#include <stdio.h>
template <class T>
void bitdump(T a){
char *x = (char*)(&a);
int count = 0;
int s = 8;
int s2 = 12;
if(sizeof(T)==16){
s = 10;
s2 = 16;
}else if(sizeof(T) == 4){
s = 4;
s2 = 9;
}
for(int i=0;i<s;i++){
for(int j=0;j<8;j++){
if(x[s-i-1] & (1<<(7-j)))printf("1");
else printf("0");
count++;
if(count==1 || count==s2)printf(" ");
}
}
printf("\n");
}
これはいくつかの浮動小数点型のビットダンプを取る超手抜きコードである。これをこんな呼び方をしてみる。
int
main(void){
bitdump(1.125F);
bitdump(1.125);
bitdump(1.125L);
}
ケチ表現の有無がわかりやすいように、1.125を表現してみた。1.125は1 + 1/8なので、2進数で表現すると「1.001」となる。これをfloat
、double
、long double
の型でビットダンプすると、こんな結果になる。
$ ./a.out
0 01111111 00100000000000000000000
0 01111111111 0010000000000000000000000000000000000000000000000000
0 011111111111111 1001000000000000000000000000000000000000000000000000000000000000
仮数部が、float
とdouble
は最上位ビットが省略された「001」に、long double
は省略されていない1001
になっていることがわかる。
同様に、1.125の2倍である2.25 (二進表記 10.01)をそれぞれダンプしてみると
0 10000000 00100000000000000000000
0 10000000000 0010000000000000000000000000000000000000000000000000
0 100000000000000 1001000000000000000000000000000000000000000000000000000000000000
と、指数部の下駄に1足されて(小数点を左に一つ移動し)、整数部が1になるように調整されてから、float
、double
はケチ表現で「001」に、long double
は最上位の整数部を省略せずに「1001」になっていることがわかる。
まとめ
x86+GCCの組み合わせにおいてlong double
が80bitの拡張倍精度浮動小数点フォーマットになっていることを確認した。拡張倍精度浮動小数点フォーマットは単精度と倍精度のフォーマットと同じだが、ケチ表現の有無が異なる。
ちなみにlong double
のサイズ(sizeof(long double)
が返す値)は16なので、16バイトを利用するのだが、内部的には10バイトしか使っておらず、残りの6バイトの値は何も保証されない。ゼロクリアもされないので、long double
の指すポインタを16バイト分ダンプすると、6バイト分は実行の度に値が変わるので注意されたい1。
-
たまたまそのデータが意味ありげなビット列だったこともあって、筆者はそれで「あれ?どうして余計なbitが立ってるんだろ?」としばらく悩んでしまった・・・ ↩