LoginSignup
14
12

More than 3 years have passed since last update.

浮動小数点数を理解する

Last updated at Posted at 2018-11-05

はじめに

浮動小数点数について説明します。
浮動小数点数はコンピュータ内部で小数点数を扱う方式の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割り当ては次の通りです。

image.png

次の式を使って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

プログラムで浮動小数点数の表現を確認します。

test.c
#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;
}
console
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  
14
12
0

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
12