浮動小数点の基礎知識
今回は浮動小数点の基礎知識について説明します。 浮動小数についてきちんと理解している人は意外と少ないのではないでしょうか。
浮動小数点は多くのプログラミングで利用されている実数の表現方式です。 C言語などの言語で使われるdoubleやfloatは浮動小数点型です。 対してintやlongなどは整数型です。
浮動小数点の仕様はIEEE 754: Standard for Binary Floating-Point Arithmeticとして策定されていますが、WikipediaのIEEE 754がわかりやすいと思います。 ざっくり説明すると、なじみのあるdoubleとfloatはそれぞれ倍精度浮動小数点、単精度浮動小数点とよばれ、それぞれ64bitと32bitで表現され、そのbit表現は符号部、指数部、小数部によって構成されます。 倍精度と単精度のそれぞれの部に割り当てられるbit数は以下のようになります。
符号部 | 指数部 | 小数部 | |
---|---|---|---|
倍精度 | 1bit | 11bit | 52bit |
単精度 | 1bit | 8bit | 23bit |
\displaystyle value = (-1)^{sign} \sum^{23}_{i=1} \left( 1 + b_i 2^{-i} \right) \times 2^{e-127}
同様に倍精度の場合には次のようになります。
\displaystyle value = (-1)^{sign} \sum^{52}_{i=1} \left( 1 + b_i 2^{-i} \right) \times 2^{e-1023}
では、実際にバイナリ表現から浮動小数点を生成してみましょう。 単精度の1.0は上記の式で
sign=0, \forall b_i=0, e=127
のときです。bit列にすると、
00111111 1000000000 00000000 00000000
です。16進数表現にすると
3f 80 00 00
ですね。これを次のようにリトルエンディアンに注意して、char型の配列に格納し、その配列の先頭アドレスをfloatのポインタとして解釈することで、バイト列をfloatとして解釈します。
#include <stdio.h>
int main()
{
char a [] = { 0x00, 0x00, 0x80, 0x3f };
float f = *((float*)a);
printf ("%f\n", f);
return 0;
}
そしてコンパイルして実行します。
$ gcc test.c
$ ./a.out
1.000000
実際に1.0が表示されました。 同様に倍精度の1.0の場合についても確認しましょう。
sign=0, \forall b_i=0, e=1023
となるので、bit列は
00111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000
となり、16進数表現では
3f f0 00 00 00 00 00 00
となります。同様にリトルエンディアンに注意してバイト列をdouble型として認識させます。
#include <stdio.h>
int main()
{
char a [] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f };
double d = *((double*)a);
printf ("%f\n", d);
return 0;
}
コンパイルして実行します。
$ gcc test.c
$ ./a.out
1.000000
1.0が表示されることを確認できます。
#include <stdio.h>
int main()
{
char a [] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f };
double d = *((double*)a);
printf ("%f\n", d);
return 0;
}
コンパイルして実行します。
$ gcc test.c
$ ./a.out
inf
infが表示されることを確認できます。
#include <stdio.h>
int main()
{
char a [] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff };
double d = *((double*)a);
printf ("%f\n", d);
return 0;
}
コンパイルして実行します。
$ gcc test.c
$ ./a.out
-inf
-inf
が表示されることを確認できます。