駄六川の草庵 トップページ > 役立たない?/プログラム等 > CvsMks概要
ごあいさつ
メニューごあいさつ、ご案内
たわごと
メニュー情報処理とビジネス
メニューその他
役立たない?
メニュープログラム等
メニュー英略語集
メニュー友人・知人リンク
プロフィール等
メニュープロフィール
メニューこのサイトについて

メニュー会員のページ

メニュー近辺のご案内

CvsMks : N88-BASIC←→IEEE 浮動小数点表現形式の変換/還元

概要

 ANSI-C 言語や Quick-BASIC あるいは Visual Basic で用いられる浮動小数点表現形式は、IEEE 形式と呼ばれます。これは、かつてMS-DOSの時代によく使われていたN88-日本語BASIC(86)というプログラミング言語(*1)における表現形式とは異なるため、過去のデータファイルを Microsoft Windows で取り扱えるよう変換する場合、問題が生じます。
 本ソースは、Windows から N88-BASIC のランダムファイルへのアクセスを主眼に、N88-BASIC における関数 CVS、CVD、MKS$、MKD$ と同等の機能を提供する DLL を生成するためのものです(*2)。生成されたDLLは、Windows 95/98/NT/XP 上で動作します。
*1. N88-日本語BASIC(86)は、当時、国内で最大のシェアを得ていたNEC製16ビットパソコンで動作しました。東芝にはT-BASIC、三菱にはM-BASICなどと、かなり似通った仕様のものがありましたが、PCメーカを替えると動作しません。現在のようにOSには依存するが、機種メーカを選ばないプログラミング言語ではありません。
*2. ちなみにN88-日本語BASIC(86)における CVI や MKI$、すなわち整数については、Cにおける signed short、Visual Basic における Integer と、同形式(同ビット列)ゆえ、いずれも2バイト単位で読み書きすればよいことになります。

所収ソース

CvsMks.C 変換/還元ルーチンの本体です。
なお DllMain 関数は、これ以上ないほど簡単な最小限のものです。マルチスレッドなど一切、考慮していませんのでご注意ください。
CvsMksX.H CvsMks.DLL 生成のためのヘッダファイル(プロトタイプ宣言)です。
CvsMks.Def DLL 生成用の関数名修飾定義です。

生成(コンパイル)法

 上記3ファイルで、Win32プロジェクトとして DLL を生成してください(*3)。

*3. Microsoft Visual C++ 6.0 / Microsoft Visual C++ .NET のいずれでも可。VC5などでも可と思われますが、未確認です。

生成後DLL所収関数

<n88CVS>
用途 N88BASIC(86) における MKS$ 文字列の数値への還元 (CVS 関数同等機能)
プロトタイプ (VC) float n88CVS( unsigned char *bytes );
(VB) Declare Function n88CVS Lib "CvsMks.Dll" (ByRef A As Any) As Single
引数 MKS$ 文字列(32ビット列)
<例> 10.5(D) = "\x00""\x00""\x28""\x84"
戻り値 還元した浮動小数点数(単精度実数値)
注記 IEEE では単精度浮動小数点数(float)を次のように表現する。
 (Upper) SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM (Lower)
  S: 符号1ビット (0 = 正、1 = 負)
  E: 指数8ビット (Bias 127 = 0x7F を加算)
  M: 仮数23ビット(+隠れビット、先頭に1)
  仮数部の先頭ビットは1を表す。
一方 N88-BASIC では、次の形式である。
 EEEE EEEE SMMM MMMM MMMM MMMM MMMM MMMM
  S: 符号1ビット (0=正、1=負)
  E: 指数8ビット (Bias 128 = 0x80 を加算)
  M: 仮数23ビット(+隠れビット、先頭に1)
  仮数部の先頭ビットは 0.5 ( = 2^-1 ) を表す。

<n88CVD>
用途 N88BASIC(86) における MKD$ 文字列の数値への還元 (CVD 関数同等機能)
プロトタイプ (VC) double n88CVD( unsigned char *bytes );
(VB) Declare Function n88CVD Lib "CvsMks.Dll" (ByRef B As Any) As Double
引数 MKD$ 文字列(64ビット列)
戻り値 還元した浮動小数点数(倍精度実数値)
注記 IEEE では倍精度浮動小数点数(double)を次のように表現する。
 (Upper) SEEE EEEE EEEE MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM (Lower)
  S: 符号1ビット (0 = 正、1 = 負)
  E: 指数11ビット (Bias 1023 = 0x3FF を加算)
  M: 仮数52ビット(+隠れビット、先頭に1)
  仮数部の先頭ビットは1を表す。
一方 N88-BASIC では、次の形式である。
 EEEE EEEE SMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM
  S: 符号1ビット (0 = 正、1 = 負)
  E: 指数8ビット (Bias 128 = 0x80 を加算)
  M: 仮数55ビット(+隠れビット、先頭に1)
  仮数部の先頭ビットは 0.0625 ( = 2^-4 ) を表す。
N88-BASIC の倍精度は、指数部は単精度と変わらないため、IEEE のように扱える範囲が広大になるわけではない。よって MKD$ → double 数値化還元で桁あふれすることは無い。

<n88MKS>
用途 float 型数値の N88BASIC(86) 形式への変換 (N88-BASIC における MKS$ 関数相当機能)
プロトタイプ (VC) unsigned char * n88MKS( float fValue, unsigned char *buf );
(VB) Declare Function n88MKS Lib "CvsMks.Dll" (ByVal C As Single, ByRef D As Any) As Long
引数 fValue (C)・・・変換元の float 型実数 (下注1)
buf (D)・・・変換後のビット列を書き込むバッファ
戻り値 変換後ビット列へのポインタ (下注2)
注記 1) 単精度実数の値範囲は次の通り。
  (IEEE) -3.402823466E+38 〜 3.402823466E+38
  (N88) -1.70141E +38 〜 1.70141E +38
 より厳密な手法を使えば N88 の限界に近づけることはできるだろうが、実用上無意味と考え、絶対値で 1.7014E+38 より大きい数値はすべて FF FF FF EB(負)、FF 7F FF EB(正)に変換することにした。
2) ビット列格納バッファへのポインタを (unsigned char *) と: したが、もちろん (char *) で、かまわない。
3) N88-BASIC では0に対しての MKS$ で結果が異なることがある。
  <例> MKS$(0) → 00 00 7A 00 (7A が E2 とかも有り)
      MKS$(0!)→ 00 00 00 00
 本プログラムでは「00 00 00 00」に固定する。

<n88MKD>
用途 double 型数値の N88BASIC(86) 形式への変換 (N88-BASIC における MKD$ 関数相当機能)
プロトタイプ (VC) unsigned char * n88MKD( double dValue, unsigned char *buf );
(VB) Declare Function n88MKD Lib "CvsMks.Dll" (ByVal E As Double, ByRef F As Any) As Long
引数 dValue (E)・・・変換元の double 型実数 (下注1)
buf (F)・・・変換後のビット列を書き込むバッファ
戻り値 変換後ビット列へのポインタ (下注2)
注記 1) 倍精度実数の値範囲は次の通り。
  (IEEE) -1.797693134862316D+308〜+1.797693134862316D+308
  (N88) -1.701411834604692D+38 〜+1.701411834604692D+38
  単精度の場合(n88MKS関数)と同様の考えで、
  -1.701411834604691D+38 未満 ・・・ FFFFFFFF FFFFFFF0
  +1.701411834604691D+38 より大 ・・・ FF7FFFFF FFFFFFF0
  に変換することにした。
2) (char *) 型、可。

使用法(1) Visual Basic から」へ進む
使用法(2) Visual C から」へ進む
役立たない?/プログラム等」へ戻る
このページの先頭に戻る


補注、参考

(限界値)
 N88-BASIC(86) の純正マニュアルには、単精度実数の範囲を次のように記しています。
    (最小値) -1.070141E+38 〜 +1.70141E+38 (最大値)
 この最小・最大値を実際に N88-BASIC で MKS$ すると、次のビット列(MSB←→LSB)が得られます。
    最小値 MKS$( -1.70141E+38 ) → FF FF FF EB (H)
    最大値 MKS$( +1.70141E+38 ) → FF 7F FF EB (H)
 n88MKS 関数では、これらのビット列を、絶対値で 1.7014E+38 より大きい数値に対し採用しました。しかし(実験してみればわかるように)、ビット列を次のように設定しても、N88-BASIC は障害(Overflow エラー等)を起こすことなく、期待通りの値に読みとってくれます。
    最小値設定 FF FF FF FF (H) → CVS → -1.70141E+38
    最大値設定 FF 7F FF FF (H) → CVS → +1.70141E+38
 すなわち、
    FF FF FF EB も FF FF FF FF も同じ -1.70141E+38 (最小値)
    FF 7F FF EB も FF FF FF FF も同じ +1.70141E+38 (最大値)
とみなしてくれるわけです。
 このエラーが生じないことを逆手にとり、変換前の float 値が N88-BASIC 単精度範囲を超えるなら FF とし、範囲内最大・最小(EB)と区別するようなこともできるでしょう。が、実用的にはまったく無意味と判断しましたので(1.7014E+38 でも十分、無駄な範囲と思えたので)、ここでは採用していません。
 ただし、所収関数を用いる前に取り扱う値が Overflow 等、障害を起こさないようアプリケーション側で考慮しておく必要があります。

 (誤差)
 ここで示したプログラムは、しょせんビット列変換に過ぎません。ゆえに変換元と変換先の値精度あるいは値範囲を越えない限り、二進法で見た場合の誤差は生じません。しかし本来的にある二進法・十進法間の変換に伴う誤差や、各処理系での丸め処理の相違に伴う誤差は逃れようがありません(別途、十進演算ルーチンを開発する以外、解決策はありません)。例えば次のような表示誤差は、必ず生じ得ます。

N88-BASICでの書き出し C(VC6.0)による読み取り
- printf -
VB6.0による読み取り
- Debug.Print -
MKD$(0.1) 0.1000000015 0.100000001490116
MKD$(0.2) 0.2000000030 0.200000002980232

darokugawa@master.email.ne.jp darokugawa@master.email.ne.jp このページ最終更新日 : October 31, 2003