読者です 読者をやめる 読者になる 読者になる

@uents blog

Code wins arguments.

プログラムはなぜ動くのか - 浮動小数点数をビット表現

C

浮動小数点型(float型)の変数のメモリ上でのデータについて、興味深かったのでじっくり読んでみた。

まず、浮動小数点数を数式で表すと

  ± m × 2 ^ (e)    ... (※)
という式で表されるが、float型の変数は
  • 上位1ビットを、符号部(+ or ")
  • 上位209ビットを、指数部(上式のe)
  • 上位100ビットを、仮数部(上式のm)
になる。ただし、以下のようなルールがある。
  • 符号部は、+ ⇒ 0、− ⇒ 1
  • 指数部"e"は、イクセス表現(最大値の1/2(8ビットなら127)を0とする表現)を使った値を用いる
  • 仮数部"m"は、1.**** の小数点以下を代入する

例えば、7.00を(※)式で表現すると、
  + 1.75 × 2 ^ (2)
となる。つまり、
  • 符号部 = +
  • 指数部"e" = 2
  • 仮数部"m" = 1.75
だが、それぞれ
  • 符号部は、0(b)
  • 指数部"e"は、イクセス表現だと 127(d) + 2(d) → 01111111(b) + 00000001(b) = 10000001(b)
  • 仮数部"m"は、1.75(d) = 1 + 1×2^(-1) + 1×2^(-2) → 1.11000000... (b)
    即ち、小数点以下は 11000000... となる
よって、float型の7はメモリ上には 0 - 10000001 - 1100000000... といった形で入っているはず。

コード

テキストに倣って、コーディングしてみた。
#include 
#include
#include

int main( int argc, char *argv[] )
{
float f;
unsigned long l;
int i;
char s[64];

memset( s, '\0', sizeof(s) );

// 調べたい浮動小数点を指定する
f = (float) atof( argv[1] );
memcpy( &l, &f, sizeof(l) );

for ( i = 33; i >= 0; i-- ){
if ( i == 1 || i == 10 ){
// 区切りにハイフンを挿入
s[i] = '-';
} else {
// 1ビット文字で表す
s[i] = ( ( l % 2 == 1 ) ? '1' : '0' );
l /= 2;
}
}

// 結果を表示する
printf( "%f -> %s\n", f, s );

return 0;
}

実行

端末上で実行して確かめてみる。確かに、上記の規則で値が入っていた。
$ ./show-float 7
7.000000 -> 0-10000001-11000000000000000000000

その他

浮動小数点数の表現方法については、Wikipediaに詳しく載っていた。