CvsMks : N88-BASIC←→IEEE 浮動小数点表現形式の変換/還元
所収ソース
CvsMks.C
/********************************************************************
file name : CvsMks.C
purpose : N88-BASIC <--> IEEE 浮動小数点表現形式の変換/還元
(CvsMks.DLL 生成用関数定義)
Version 0.3 / October 31, 2003
--------------------------------------------------------------------
history : V0.1/1999-10-26/ 非DLL化ソース CVS.C、MKS.C 転用。
: V0.2/1999-10-27/ コメント修正等。
: V0.3/2003-10-31/ ホームページ用コメント補正。
********************************************************************/
#include <windows.h>
#include "CvsMksX.h" /* 本 DLL 作成用(Export用)ヘッダ */
static int CheckZeroUChar( unsigned char * );
static int CheckZeroDouble( double );
/********************************************************************
function : DllMain
purpose : Dll Entry Point
********************************************************************/
BOOL WINAPI DllMain( HINSTANCE hInstDll, DWORD reason, LPVOID reserved )
{
switch( reason )
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
/********************************************************************
function : n88CVS
purpose : N88BASIC(86) における MKS$ 文字列の数値への還元
: (CVS 関数同等機能)
param.: MKS$ 文字列(32ビット列)
: <例> 10.5(D) = "\x00""\x00""\x28""\x84"
ret.val.: 還元した浮動小数点数(単精度実数値)
remark[1]: IEEE では単精度浮動小数点数(float)を次のように表現する。
: (Upper) (Lower)
: SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
: 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 ) を表す。
remark[2]: (別解法)もとのビット列から符号、指数、仮数を抽出後、
: fVal = (float)( sign *
: ( ldexp( 0.5 * mantissa * FLT_EPSILON, exponent ) ) );
********************************************************************/
DllExport float WINAPI n88CVS( unsigned char *mks )
{
union BYTES_TAG1{
unsigned char sByte[4];
unsigned long ulTemp;
} LongBytes;
union BYTES_TAG2{
unsigned long ulTemp;
float fValue;
} FloatBytes;
unsigned char *ptr; /* 引数参照用ポインタ */
int i;
unsigned long sign; /* 符号 */
unsigned long exponent; /* 指数 */
unsigned long mantissa; /* 仮数 */
ptr = mks;
for( i = 0; i < 4; i++ ) LongBytes.sByte[i] = *ptr++;
/* 0x00800000 で符号signを抽出 0 = 正、else 負 */
if( LongBytes.ulTemp & 0x00800000 ) sign = 0x80000000; /* 負 */
else sign = 0x00000000; /* 正 */
if( LongBytes.ulTemp == 0L )
{
exponent = 0L; /* 規定 */
mantissa = 0L;
}
else
{
/* 0xff000000 で指数exponentを抽出 >> 24 */
exponent = ( ( LongBytes.ulTemp & 0xff000000 )
- 0x80000000 + 0x7f000000 - 0x01000000 ) >> 1;
/* N88 Bias IEEE Bias 隠れビット相違 */
/* 0x007fffff で仮数mantissa を抽出 */
mantissa = ( LongBytes.ulTemp & 0x007fffff );
}
FloatBytes.ulTemp = exponent | sign | mantissa;
return ( FloatBytes.fValue );
}
/********************************************************************
function : n88CVD
purpose : N88BASIC(86) における( MKD$ 文字列の数値への還元)
: (CVD 関数同等機能)
param.: MKD$ 文字列(64ビット列)
ret.val.: 還元した浮動小数点数(倍精度実数値)
remark[1]: IEEE では倍精度浮動小数点数(double)を次のように表現する。
: (Upper)
: SEEE EEEE EEEE MMMM MMMM MMMM MMMM MMMM (Lower)
: MMMM MMMM MMMM MMMM MMMM MMMM MMMM MMMM
: 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 数値化還元で桁あふれすることは無い。
remark[2]: (別解法)もとのビット列から符号、指数、仮数を抽出後、
: dVal = ldexp( 0.0625*mantissa*DBL_EPSILON, exponent );
********************************************************************/
DllExport double WINAPI n88CVD( unsigned char *mkd )
{
union BYTES_TAG1{
unsigned char sByte[4];
unsigned long ulTemp;
} LongBytes;
union BYTE_TAG2{
unsigned char sByte[8];
double dValue;
} DoubleBytes;
char *rp; /* 引数参照用ポインタ */
char *wp; /* 変数設定用ポインタ */
int i;
unsigned long sign; /* 符号 */
unsigned long exponent; /* 指数 */
unsigned long mantissa1; /* 仮数1 上位 23 or 20 bits */
unsigned long mantissa2; /* 仮数2 下位 32 bits */
unsigned long longTemp; /* 計算値一時保管用 */
if( CheckZeroUChar( mkd ) != FALSE ) /* 0ならば */
{ /* 規定値 */
sign = 0x00000000;
exponent = 0x00000000;
mantissa1 = 0x00000000;
mantissa2 = 0x00000000;
}
else
{
rp = mkd + 4;
wp = LongBytes.sByte;
for( i = 0; i < 4; i++ ) *wp++ = *rp++;
/* 0x00800000 で符号 sign を抽出 0→正、else→負 */
if( LongBytes.ulTemp & 0x00800000 ) sign = 0x80000000; /* 負 */
else sign = 0x00000000; /* 正 */
/* 0xff000000 で指数 exponent を抽出 >> 24 */
longTemp = ( LongBytes.ulTemp & 0xff000000 ) >> 24;
longTemp -= 0x00000080; /* N88 での Bias 値を減ずる */
longTemp += 0x000003FF; /* IEEE での Bias 値を加える */
longTemp -= 0x00000001; /* 隠れビット相違(2^-4)、調整 */
/* 指数を IEEE 形式に */
exponent = longTemp << 20;
/* 仮数の先行部分を抽出 */
/* mantissa2 のため Lower 3 bits を左シフトの後、一時保管 */
longTemp = ( LongBytes.ulTemp & 0x00000007 ) << 29;
mantissa1 = ( LongBytes.ulTemp & 0x007fffff ) >> 3;
/* 仮数の後方部分 32 bits を抽出、3 bit 右シフト */
/* Lower 3 bits は捨てる */
/* 先に保管していた 3 bits とビット合成 */
rp = mkd;
wp = LongBytes.sByte;
for( i = 0; i < 4; i++ ) *wp++ = *rp++;
mantissa2 = ( LongBytes.ulTemp >> 3 ) | longTemp;
}
/* 低位バイト格納 */
rp = (unsigned char *)( &mantissa2 );
for( i = 0; i < 4; i++ ) DoubleBytes.sByte[i] = *rp++;
/* 高位バイト格納 */
longTemp = sign | exponent | mantissa1; /* ビット合成 */
rp = (unsigned char *)( &longTemp );
for( i = 4; i < 8; i++ ) DoubleBytes.sByte[i] = *rp++;
return ( DoubleBytes.dValue );
}
/********************************************************************
function : CheckZeroUChar
purpose : 8バイト(double 用ビット列)が、ゼロか否か判定
ret.val.: 1 ... 8バイト全てが 0x00(=Double 値に変換すると0)
: 0 ... 0x00 以外有り
********************************************************************/
static int CheckZeroUChar( unsigned char *bytes )
{
int i;
int iRet = TRUE;
unsigned char *ptr;
ptr = bytes;
for( i = 0; i < 8; i++ )
{
if( *ptr != 0x00 )
{
iRet = FALSE;
break;
}
ptr++;
}
return iRet;
}
/********************************************************************
function : n88MKS
purpose : float 型数値の N88BASIC(86) 形式への変換
: (N88-BASIC における MKS$ 関数相当機能)
param.: fValue ... 変換元の float 型実数 (rem.1)
: buf ...... 変換後のビット列を書き込むバッファ
ret.val.: 変換後ビット列へのポインタ (rem.2)
remark[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(正)
: に変換することにした。
remark[2]: ビット列格納バッファへのポインタを (unsigned char *) と
: したが、もちろん (char *) で、かまわない。
remark[3]: N88-BASIC では0に対しての MKS$ で結果が異なることがある。
: <例> MKS$(0) → 00 00 7A 00 (7A が E2 とかも有り)
: MKS$(0!)→ 00 00 00 00
: 本プログラムでは「00 00 00 00」に固定する。
********************************************************************/
DllExport unsigned char * WINAPI n88MKS( float fValue, unsigned char *buf )
{
union BYTES_TAG{
float fTemp;
unsigned long ulTemp;
} FloatBytes;
int i;
unsigned char *ptr;
unsigned long sign = 0x00000000; /* 符号 */
unsigned long exponent; /* 指数 */
unsigned long mantissa; /* 仮数 */
unsigned long lValue;
if( fValue < 0.0 )
{
sign = 0x00800000;
fValue *= -1.0;
}
if( fValue > 1.7014E+38 )
{
exponent = 0xff000000;
mantissa = 0x007fffeb;
}
else
{
FloatBytes.fTemp = fValue;
if( FloatBytes.ulTemp == 0L )
{ /* 規定:論理ゼロなら */
exponent = 0x00000000; /* 00 00 00 00 (H) とする */
}
else
{
exponent = ( FloatBytes.ulTemp & 0x7f800000 ) << 1;
exponent += ( 0x81000000 - 0x7F000000 ); /* Bias 調整 */
}
mantissa = FloatBytes.ulTemp & 0x007fffff;
}
lValue = exponent | sign | mantissa;
ptr = (unsigned char *)( &lValue );
for( i = 0; i < 4; i++ ) buf[i] = *ptr++;
return ( &buf[0] );
}
/********************************************************************
function : n88MKD
purpose : double 型数値の N88BASIC(86) 形式への変換
: (N88-BASIC における MKD$ 関数相当機能)
param.: dValue ... 変換元の double 型実数 (rem.1)
: buf ...... 変換後のビット列を書き込むバッファ
ret.val.: 変換後ビット列へのポインタ (rem.2)
remark[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
: に変換することにした。
remark[2]: (char *) 型、可。
********************************************************************/
DllExport unsigned char * WINAPI n88MKD( double dValue, unsigned char *buf )
{
union BYTES_TAG{
unsigned char sByte[4];
unsigned long ulTemp;
} LongBytes;
unsigned char *ptr;
int i;
unsigned long sign = 0x00000000; /* 符号(初期値は正)*/
unsigned long exponent; /* 指数 */
unsigned long mantissa1; /* 仮数1 上位 23 or 20 bits */
unsigned long mantissa2; /* 仮数2 下位 32 bits */
unsigned long longTemp; /* 計算値一時保管用 */
if( dValue < 0.0 )
{
sign = 0x00800000;
dValue *= -1.0;
}
if( dValue > 1.701411834604691e+38 )
{
exponent = 0xff000000;
mantissa1 = 0x007fffff;
mantissa2 = 0xfffffff0;
}
else
{
if( CheckZeroDouble( dValue ) )
{ /* 値0は規定値 */
sign = 0L;
exponent = 0L;
mantissa1 = 0L;
mantissa2 = 0L;
}
else
{
/* 下位4バイトを long としてビット処理 */
ptr = (unsigned char *)( &dValue );
for( i = 0; i < 4; i++ ) LongBytes.sByte[i] = *ptr++;
/* 仮数部を、N88 形式上の上位、下位各4バイトに分離 */
/* ビット位置調整 */
mantissa1 = ( LongBytes.ulTemp & 0xE0000000 ) >> 29;
mantissa2 = LongBytes.ulTemp << 3;
/* 上位4バイトを long としてビット処理 */
ptr = (unsigned char *)( &dValue );
ptr += 4;
for( i = 0; i < 4; i++ ) LongBytes.sByte[i] = *ptr++;
/* 指数部抽出、ゲタ変換 */
longTemp = ( LongBytes.ulTemp & 0x7FF00000 );
longTemp -= 0x3ff00000; /* IEEE 用ゲタを減ずる */
longTemp += 0x08000000; /* N-88 用ゲタをはかす */
longTemp += 0x00100000; /* 隠れビット相違(2^-4)、調整 */
exponent = ( longTemp << 4 );
/* N88 上の上位4バイトに置くべき仮数の合成 */
longTemp = ( LongBytes.ulTemp & 0x000fffff ) << 3;
mantissa1 |= longTemp;
}
}
longTemp = exponent | sign | mantissa1;
ptr = (unsigned char *)( &mantissa2 );
for( i = 0; i < 4; i++ ) buf[i] = *ptr++;
ptr = (unsigned char *)( &longTemp );
for( i = 4; i < 8; i++ ) buf[i] = *ptr++;
return ( &buf[0] );
}
/********************************************************************
function : CheckZeroDouble
purpose : double 値(8バイト)が、ゼロか否か判定
param. : dVal ... 判定したい double 値
ret.val.: TRUE ... 引数は0である
: FALSE .. 引数は0ではない
remark : 本来、実数比較は二進数・十進数間の変換誤差を考慮し、
: 0±DBL_EPSILON 範囲内か否かを調べなければならない。
: しかし本ソースでは、ビット列としてゼロが連続している
: のか否かが問題であるので、以下のようにした。
********************************************************************/
static int CheckZeroDouble( double dVal )
{
int i;
int iRet = TRUE;
unsigned char *ptr;
ptr = (unsigned char *)( &dVal );
for( i = 0; i < 8; i++ )
{
if( *ptr != 0x00 )
{
iRet = FALSE;
break;
}
ptr++;
}
return iRet;
}
CvsMks.H
/********************************************************************
file name : CvsMksX.H
purpose : N88-BASIC <--> IEEE 浮動小数点表現形式の変換/還元
CvsMks.DLL 生成のためのヘッダファイル(プロトタイプ宣言)
Version 0.2 / October 31, 2003
--------------------------------------------------------------------
history : V0.1/1999-10-26/ 非DLL化ソース CVS.H、MKS.H 転用。
: V0.2/2003-10-31/ ホームページ用コメント改変。
********************************************************************/
#include <windows.h>
#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif
#define DllExport __declspec( dllexport )
BOOL WINAPI DllMain( HINSTANCE, DWORD, LPVOID );
DllExport float WINAPI n88CVS( unsigned char * );
DllExport double WINAPI n88CVD( unsigned char * );
DllExport unsigned char * WINAPI n88MKS( float, unsigned char * );
DllExport unsigned char * WINAPI n88MKD( double, unsigned char * );
CvsMks.Def
LIBRARY CVSMKS
DESCRIPTION "Floating Point / N88-BASIC <-> IEEE"
EXPORTS
n88CVS = _n88CVS@4
n88CVD = _n88CVD@4
n88MKS = _n88MKS@8
n88MKD = _n88MKD@12
このページの先頭へ戻る
「CvsMks概要」ページへ戻る
「役立たない?/プログラム等」のページへ戻る
 |
darokugawa@master.email.ne.jp |
このページ最終更新日 : October 31, 2003 |
|