LoginSignup
14

More than 5 years have passed since last update.

long doubleと拡張倍精度浮動小数点数フォーマット

Posted at

はじめに

x86系では、long doubleが内部80bit精度の拡張倍精度浮動小数点数フォーマットが採用されている。おそらくx87系命令の内部精度が80bitなのと合わせてあるのだと思われる。それをざっと確認しておく。

拡張倍精度浮動小数点数フォーマットとは

Wikipediaのこの図の通りなんだけれど、要するに

  • 符号 1bit
  • 指数部 15bit
  • 仮数部 64bit

の合計80bitの表現になっている。ここで気をつけなくてはならないのは、この仮数部はいわゆる「ケチ表現」になっておらず、正規化した数字の最上位ビット1を省略しないこと。従って、単精度、倍精度と仮数部のbit数を比較するなら、63bitの精度になっている。

long double

環境に依存するのだろうが、手元のMacやx86 Linux+GCCの組み合わせでは、long doubleのサイズは16バイト、精度は80bitの拡張倍精度浮動小数点数フォーマットになっていた。

拡張倍精度浮動小数点数フォーマットを見てみる

というわけでそのビット表現を見てみる。こんなコードを書いてみよう。

test.cpp
#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」となる。これをfloatdoublelong doubleの型でビットダンプすると、こんな結果になる。

$ ./a.out  
0 01111111 00100000000000000000000
0 01111111111 0010000000000000000000000000000000000000000000000000
0 011111111111111 1001000000000000000000000000000000000000000000000000000000000000

仮数部が、floatdoubleは最上位ビットが省略された「001」に、long doubleは省略されていない1001になっていることがわかる。

同様に、1.125の2倍である2.25 (二進表記 10.01)をそれぞれダンプしてみると

0 10000000 00100000000000000000000
0 10000000000 0010000000000000000000000000000000000000000000000000
0 100000000000000 1001000000000000000000000000000000000000000000000000000000000000

と、指数部の下駄に1足されて(小数点を左に一つ移動し)、整数部が1になるように調整されてから、floatdoubleはケチ表現で「001」に、long doubleは最上位の整数部を省略せずに「1001」になっていることがわかる。

まとめ

x86+GCCの組み合わせにおいてlong doubleが80bitの拡張倍精度浮動小数点フォーマットになっていることを確認した。拡張倍精度浮動小数点フォーマットは単精度と倍精度のフォーマットと同じだが、ケチ表現の有無が異なる。

ちなみにlong doubleのサイズ(sizeof(long double)が返す値)は16なので、16バイトを利用するのだが、内部的には10バイトしか使っておらず、残りの6バイトの値は何も保証されない。ゼロクリアもされないので、long doubleの指すポインタを16バイト分ダンプすると、6バイト分は実行の度に値が変わるので注意されたい1


  1. たまたまそのデータが意味ありげなビット列だったこともあって、筆者はそれで「あれ?どうして余計なbitが立ってるんだろ?」としばらく悩んでしまった・・・ 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14