【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

IEEE 754 単精度浮動小数点数

C言語浮動小数点数を算出するプログラムを書いてみました。

/* This program is in the public domain. */

#include <stdio.h>
#include <limits.h>

#define EXP_DIGIT 8
#define EXP_MAX ((1 << EXP_DIGIT) - 1)
#define SGNF_DIGIT 23
#define SGNF_MAX ((1 << SGNF_DIGIT) - 1)

void printfloat(float f) {
    unsigned int v = *(int *)&f;
    printf("%f => %d-%x-%x\n", f, v >> 31, (v >> 23) & 0xff, v & 0x7fffff);
}

void Float(int s, int e10) {
    int sign = 0, e2 = 0, ss;
    if (s == 0) e10 = 0;
    if (s < 0) sign = 1, s = -s;
    while (s % 10 == 0) e10++, s /= 10;
    if (e10 < 0) {
        while (e10 != 0) {
            e2--, s <<= 1;
            ss = s % 10;
            if (ss == 0 || s > INT_MAX / 2)
                e10++, s = (s / 10) + (ss >= 5 ? 1 : 0);
        }
    } else if (e10 > 0) {
        while (e10 != 0) {
            if (s & 1 == 1 && s <= INT_MAX / 10)
                e10--, s *= 10;
            e2++, s >>= 1;
        }
    }
    while (s > 0 && s <= SGNF_MAX / 2) e2--, s <<= 1;
    while (s > SGNF_MAX * 4 + 3) e2++, s >>= 1;
    while (s > SGNF_MAX * 2 + 1) e2++, s = (s >> 1) | (s & 1);
    int result = (sign << 31) | ((e2 + SGNF_DIGIT + EXP_MAX / 2) << 23) | (s & SGNF_MAX);
    printfloat(*(float *)&result);
}

int main() {
    printfloat(-0.1); Float(-1, -1);
    printfloat(3.14159265); Float(314159265, -8);
    printfloat(16777214); Float(16777214, 0);
    printfloat(16777215); Float(16777215, 0);
    printfloat(16777216); Float(16777216, 0);
    printfloat(16777217); Float(16777217, 0);
    printfloat(16777218); Float(16777218, 0);
    return 0;
} 

二進数は自然数でしか扱ったことがなかったため、小数点表示をどう処理するか悩みました。二進数の0.1が10進数の0.5だとか、考えるだけで混乱します。そのためすべて整数だけで処理してみました。

プログラムの処理内容は以下の通りです。以下では説明の便宜上変数を a, b, c としていますが、プログラム中では s, e10, e2 に該当します。

  1. 浮動小数点数を整数 a, b により a × 10b と表す。
    例: 0.5 → 5 × 10-1 (a = 5, b = -1)
  2. c = 0 として a × 10b × 2c の形に変形する。
    例: 5 × 10-1 × 20
  3. b = 0 になるように a と c の値を調整する。
    例: 5 × 10-1 × 20 → 10 × 10-1 × 2-1 → 1 × 100 × 2-1
  4. a × 2c が得られる。
    例: 1 × 2-1
  5. 最上位ビットの位置を合わせて浮動小数点表示にする。

実際にやってみて0.1が循環小数になることが実感できました。10進数ではきれいに処理できる1/10の計算で循環が出てきたのが興味深いです。