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

メニュー会員のページ

メニュー近辺のご案内

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 darokugawa@master.email.ne.jp このページ最終更新日 : October 31, 2003