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

kivantium活動日記

プログラムを使っていろいろやります

浮動小数点数

この記事はkivantium Advent Calendarの12日目です。

昨日までで整数の四則演算のやり方を紹介しました。
今日からは浮動小数点数の使い方を紹介します。

IEEE-754

浮動小数点数の表し方はコンピュータの歴史の中で何種類かありましたが、現在主流となっているのはIEEE-754と呼ばれるフォーマットです。

特によく使われるのは単精度と倍精度の2種類です。
どちらも符号部・指数部・仮数部からなり、精度によって大きさが異なります。
有効数字を保つために科学では$1.234\times10^3$のような表し方をしますが、それを2進数で行ったのがこのIEEE-754です。

単精度

単精度は32bitからなり、以下のようなフォーマットを持ちます。

  • 符号部 (sign) は0が正、1が負を表します。
  • 指数部 (exponent) は127をゼロとするバイアス付きの値として表され、-128から127を表します。
  • 仮数部 (fraction)は精度を上げるために最上位の1を省略した2進数として表します。 1.f_1f_2f_3\dots
  • このフォーマットで表される値は (-1)^{\text{sign}}(1+\sum _{{i=1}}^{{23}}\ f_{{i}}2^{{-i}})\times 2^{{(\text{exponent}-127)}}となります。

倍精度

倍精度も基本は単精度と同じです。指数部のバイアスは1023に変わるので、表す値は (-1)^{\text{sign}}(1+\sum _{{i=1}}^{{52}}\ f_{{i}}2^{{-i}})\times 2^{{(\text{exponent}-1023)}}となります。

非正規化数

浮動小数点数では正規化数(さっきの式で表される数)の範囲を超えたときのためにいくつか正規化数以外の数の表現方法が用意されています。単精度の場合は

種類 指数部 仮数
ゼロ 0 0
非正規化数 0 0以外
正規化数 1〜254 任意
無限大 255 0
NaN 255 0以外

となります。

  • 非正規化数は正規化数で表される範囲よりも0に近いときに使われ、 (-1)^{\text{sign}}(\sum _{{i=1}}^{{23}}\ f_{{i}}2^{{-i}})\times 2^{{(-126)}}を表します。
  • 無限大はとても大きい数を表すときに使います。
  • NaNはNot a Numberの意味で0÷0のような不正な演算の結果として使われることがあります。

ビット表現を覗いてみる

ビット表現を確認する方法として、共用体を使う方法があります。
これは共用体の実装依存の挙動を利用しているほか、floatが32bitであることを前提としているなどお行儀の良くないコードなのですが多くの環境で動くようです。

#include <stdio.h>
#include <stdint.h>

union Float2Int {
    uint32_t int_value;
    float float_value;
};

int main (int argc, char *argv[]) {
    union Float2Int a;
    a.float_value = 1.25;
    for (int i=31; i>=0; i--) {
        printf("%d", (a.int_value >> i) & 1);
        if(i==31 || i==23) printf(" ");
    }
    printf("\n");
    printf("exponent: %d\n", (a.int_value >> 23) & 0xff);
}

僕の環境では

0 01111111 0100000000000000000000
exponent: 127

と出力されました。
2進数での`1.01`は10進数の1.25に対応するのでIEEE-754フォーマットに従って格納されていそうだと分かります。

次回は浮動小数点数同士の足し算をします。