「BASIC でファイルに落とした浮動小数点の数値データを Unix の C で読まなけ ればならなくなったのですが、どうしたら良いのでしょう。BASIC のプログラムは こんなものです」
OPEN "DATA" AS #1 RN=1 .. FOR I=1 TO N FIELD #1,4*(I-1) AS A$(1),4 A$(2) LSET A$(2)=MKS$(D(I)) NEXT I PUT #1,RN ..なんて言われて、20 年以上前に当時のフォーマットを調べたことがあったのを想 いだしました。
サンプルデータを送ってもらったところ、当時と同じらしく、単精度実数 4 バイ トの内容は、16 進表記で書くと、次のとおりと考えてよいようです。
E1 M1 M2 M3 | +---+---+ | +-------- mantissa +----------------- exponent
「exponent」(指数部)は 0x80 の下駄履き表示で、0x7f なら 2^(-1)、0x81 なら 2^1 になります。つまり、2^(E1 - 0x80) です。0x80 のときは例外で、数値 そのものが 0 であることにします。
「mantissa」(仮数部)は「normalized」(正規化)されていますから、
.1XXXXXX.. (X = 0 or 1)という2進数で、最上位の桁は必ず 1 ですから、ここに符号を入れてしまうこと にして、
sign = (M1 & 0x80) ? -1 : 1というルールにします。
倍精度実数の場合も同じやり方で、M3 の後ろに M5, M6, M7, M8 が追加されるだ けです。
ここまでわかれば後は簡単ですが、読み取ったバイト列を実数に変換するプログラ ム例を付けておきます。「Intel」の「x86」プロセサは下位バイトが先頭になるこ とに注意してください。先程の図と逆順になります。
double val(s) char *s; { int i; long k, x; double e, sign, m, pow(); if (s[3] == 0x00) { m = e = 0.0; sign = 1.0; } else { sign = (s[2] & 0x80) ? -1.0 : 1.0; x = 0x800000 | ((s[2] & 0xff) << 16) | ((s[1] & 0xff) << 8) | (s[0] & 0xff); k = 0x800000; for (w = 0.5, x = 0.0, i = 0; i < 24; k >>= 1, w *= 0.5, i++) if (x & k) m += w; e = pow(2.0, (double)((s[3] & 0xff) - 0x80)); } return sign * m * e; }