はじめに
浮動小数点数について説明します。
浮動小数点数はコンピュータ内部で小数点数を扱う方式の1つです。
IEEE 754で規格化されています。
表記
ここで用いる表記を説明します。
10進数と2進数を区別するため、2進数の場合、後ろにbをつけます。
例. 11b = 3
記号の下付き数値は対応するbitを表します。
例. $f_0$はfを表すbitの0番目です。
format
浮動小数点数を表すbit列を符号s、指数e、有効数字f の3つの部分に分けます。
e / f の bit lengthに応じていくつかのformatがあります。biasは後で説明します。
format | s | e | f | s+e+f | bias |
---|---|---|---|---|---|
half | 1 | 5 | 10 | 16 | 15 |
single | 1 | 8 | 23 | 32 | 127 |
double | 1 | 11 | 52 | 64 | 1023 |
quad | 1 | 15 | 112 | 128 | 16383 |
format の違いは e / f のbit length の違いのみであり、
考え方は同じため 以降は singleについてのみ説明します。
s / e / f の bit割り当ては次の通りです。
次の式を使ってbit列と小数点数を対応付けます。2種類の方式があります。
それぞれ正規化数、非正規化数といいます。127はbiasです。
$(-1)^s(1.f_{22}f_{21}...f_0)_b 2^{(e-127)}$ ... 正規化数
$(-1)^s(0.f_{22}f_{21}...f_0)_b 2^{-126}$ ... 非正規化数
sは符号です。
s = 0 は正の数、s = 1 は負の数
を表します。
e / f の組み合わせによってbit列の意味が変化します。
対応関係を以下に示します。e = 0 / e = 255は特別な意味を持ちます。
e | f | bit列の意味 |
---|---|---|
0 | 0 | +0 or -0を表します |
0 | !0 | 非正規化数を表します |
255 | 0 | +inf or -infを表します |
255 | !0 | NaNを表します |
1~254 | any | 正規化数を表します |
fは有効数字です。
1.5の場合は f = 100 0000 0000 0000 0000 0000 b となります。
具体例
具体例で浮動小数点数表現を理解しましょう。
10進数の12.375を浮動小数点数で表します。
10進数を2進数にします。
12.375 = 12 + 0.375 = 1100b + 0.011b = 1100.011b
正規化数にします。つまり1.xxxxの形にします。
1100.011b = 1.100011b * 2^11b と変形します
よって
f = 100011b
eについては bias = 127 = 111 1111b であり、
11b = e - 127 のため、
移項して e = 11b + 127 = 11b + 111 1111b = 1000 0010b となります。
最終的には次のバイナリになります
0 10000010 10001100000000000000000 b
0100 0001 0100 0110 0000 0000 0000 0000 b = 0x41460000
sample code
プログラムで浮動小数点数の表現を確認します。
#include <stdio.h>
#include <stdint.h>
void print_binary(uint32_t val)
{
int i;
for (i=0; i<32; i++) {
uint32_t tmp = ((val>>(31-i))&1);
printf("%01d", tmp);
if (i == 0 || i == 8) {
printf(" ");
}
}
}
void print(float f)
{
float _f = f;
uint32_t* p = (uint32_t*)&_f;
print_binary(*p);
printf(" = 0x%08x = %e ", *p, _f);
printf("\n");
}
int main()
{
float f;
uint32_t* p = (uint32_t*)&f;
printf("s eeeeeeee fffffffffffffffffffffff b\n");
print(12.375f);
print(1.f);
print(-1.f);
// +0 s=0 e=0 f=0
*p = 0x00000000;
print(f);
// +inf s=0 e=ff f=0
*p = 0x7f800000;
print(f);
// NaN s=0 e=ff f=1
*p = 0x7f800001;
print(f);
// largest s=0 e=fe f=7fffff
*p = 0x7f7fffff;
print(f);
// smallest s=0 e=00 f=000001
*p = 0x00000001;
print(f);
return 0;
}
gcc test.c && ./a.out
s eeeeeeee fffffffffffffffffffffff b
0 10000010 10001100000000000000000 = 0x41460000 = 1.237500e+01
0 01111111 00000000000000000000000 = 0x3f800000 = 1.000000e+00
1 01111111 00000000000000000000000 = 0xbf800000 = -1.000000e+00
0 00000000 00000000000000000000000 = 0x00000000 = 0.000000e+00
0 11111111 00000000000000000000000 = 0x7f800000 = inf
0 11111111 00000000000000000000001 = 0x7f800001 = nan
0 11111110 11111111111111111111111 = 0x7f7fffff = 3.402823e+38
0 00000000 00000000000000000000001 = 0x00000001 = 1.401298e-45