/* 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 に該当します。
- 浮動小数点数を整数 a, b により a × 10b と表す。
例: 0.5 → 5 × 10-1 (a = 5, b = -1) - c = 0 として a × 10b × 2c の形に変形する。
例: 5 × 10-1 × 20 - b = 0 になるように a と c の値を調整する。
例: 5 × 10-1 × 20 → 10 × 10-1 × 2-1 → 1 × 100 × 2-1 - a × 2c が得られる。
例: 1 × 2-1 - 最上位ビットの位置を合わせて浮動小数点表示にする。
実際にやってみて0.1が循環小数になることが実感できました。10進数ではきれいに処理できる1/10の計算で循環が出てきたのが興味深いです。