「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;
}