top page > la vie en rose > here

qlib の活用


概要

qlib の使用上の諸注意や補足について述べます.


目次

1. 利用できる関数,定数の差異
2. 定数の取り扱い方
3. 補完の必要性

1. 利用できる関数,定数の差異

c++ を用いるならば,以下の点にさえ注意すれば,qfloat を c 言語の標準の浮動小数点数であるかのように使えます. 従って,既存の double を用いたプログラムを qfloat を用いるように書き換えたり,#ifdef 等を使って両者の source を統一するのも比較的容易です.

変数名 math.h 内容
 qzero なし  0.0
 qhalf なし  0.5
 qone なし  1.0
 qtwo なし  2.0
 qthree なし  3.0
 qnine なし  9.0
 q32 なし 32.0
 qlog2 M_LN2 log(2)
 qsqrt2 M_SQRT2 sqrt(2)
 oneopi M_2_SQRTPI 2/sqrt(pi)
 qpi M_PI pi (円周率)
 qeul M_E e (Napier の数)

2. 定数の取り扱い方

ソースの中の定数,例えば 3.0double3.0Llong double の精度しかありません. 定数の値が 2^n (2n 乗) の場合,または倍精度で「きれいに」表現できる数 (比較的桁の小さな整数) ならば良いのですが,そうでない場合には単に定数を代入したのでは精度が落ちます.

定数の精度を落とさないためには,次のような方策があります.

  • 関数 asctoq()ltoq() を利用する.
  • 可能ならば,上の表の「定数」から換算する.

その定数を頻繁に使うなら,qconst.c に追加するのも合理的でしょう.
この定数の取り扱いの煩わしさこそが,qlib の一番の弱点であると考えます.

以下に,いくつかの例を示します. このソースでは値の出力を省きましたが,下記の関数 ana_qfloat() を使って,各々の場合にどうなるか確認してみて下さい.

#include <stdio.h>

extern qfloat qone, qthree;

main(void)
{
   long   l;
   double d;
   qfloat q,p;

/*  以下の二つは "asctoq conversion error" になる. */
   asctoq("3.0L",&q.a);      /*  L は使えないし,不要である.  */
   asctoq("1.0/3.0",&q.a);   /*  文字列の中で演算はできない. */


/*  q に 3 を代入する.  */
   l = 3;  ltoq(&l,&q.a);  /*  OK  */
   asctoq("3.0",&q.a);     /*  OK  */
   q = "3.0";              /*  OK  */
   d = 3.0;  q = d;        /*  倍精度の 3.0 が「きれい」なので精度の低下はない.  */
   q = 3.0L;               /*  拡張倍精度の 3.0L が「きれい」なので精度の低下はない.  */
   q = 3.0;                /*  OK  */


/*  q に 1/3 を代入する.  */
   d = 1.0/3.0;  q = d;    /*  倍精度になる.  */
   q = 1.0/3.0;            /*  倍精度になる.  */
   q = qone/qthree;        /*  OK  */
   q = 1.0/qthree;         /*  OK  */
   p = 3.0;  q = 1.0/p;    /*  OK  */

   return 0;
}

変数 qfloat の中身は,次のようにすれば qconst.c の書式で出力できます. このようにして,定数の変換を点検することができます.

#include <stdio.h>
#include "qfloat.h"

/*  以下の三つは qhead.h に定義されている. */
typedef unsigned int QELT;
#define EXPONE 0x8001
#define NQ     14

/*  function prototypes  */
void ana_qfloat( qfloat x );


int main(void)
{
   qfloat q;

   q = 3.0;  q /= 10.0;  /*  精度良く 0.3 が実現できる.  */
   ana_qfloat(q);

   q = 0.3;              /*  倍精度の 0.3 を代入するので精度が悪い.  */
   ana_qfloat(q);

   return 0;
}

void ana_qfloat( qfloat x )  /*  dump qfloat for qconst.c  */
{
   int  i,e;
   QELT a;

   qprint(40,"x = ",x,"\n");

   printf("{\n");
   printf("%d,",x.a.ar[0]);                     /*  sign, 0 or -1  */

   a = x.a.ar[1];                          /*  exponent with bias  */
   e = a - EXPONE;                         /*  exponent            */

   if      ( e == 0 )  printf("EXPONE,");
   else if ( e >  0 )  printf("EXPONE+%d,",e);
   else                printf("EXPONE%d,",e);

   printf("%d,",x.a.ar[2]);               /*  unused, to be zereo  */

   for ( i = 3; i < NQ; i++ )  {                     /*  mantissa  */
      printf("0x%08x",x.a.ar[i]);

      if      ( i == 7    )  printf(",\n");
      else if ( i != NQ-1 )  printf(",");
      else                   printf("};\n");
   }
}

3. 補完の必要性

qlib は c から使う環境よりも c++ から使う環境の方が完成度が少しだけ低いです. しかし,c にて実現されていることは容易に c++ でも実現できます. 以下に示すような要領で補完して使いましょう.

qfloat.hqlib に存在する算術関数すべてを反映しているわけではありません. (例えば log10(), jn() 等がない.) そのような関数を使う場合には,次のようにします. (実際には,qfloat.h を変更した方が合理的でしょう.)

#include <stdio.h>
#include "qfloat.h"

extern "C" {  // qjn() は実数次の Bessel 関数
   void qjn( const qfloat *, const qfloat *, qfloat * );
};

inline qfloat jn( const qfloat &n, const qfloat &x ) // qjn() により jn() を定義
{
   qfloat y;
   qjn(&n,&x,&y);
   return(y);
}

main(void)
{
   int i;
   qfloat s[10],t;

   t = 50.0;
   for ( i = 0; i < 10; i++ )  s[i] = jn((qfloat)i,t);

   return 0;
}

また qprint() ですが,文字列として用意されたバッファが意外に小さいので,これを越える文字数を出力する場合にはサイズを変える必要があります. 具体的には,それを記述した qfloat.h の次の箇所を変更します.

inline void qprint ( const int n, const char *s1, const qfloat &x,
                     const char *s2)
{
  char str[30];             ← 必要に応じてこの配列を大きくする.
  qtoasc (&x, str, n);
  printf ("%s %s %s", s1, str, s2);
}

これらの修正のために,qlib の導入 2.3 ソースの修正 に patch を置きましたので,ご活用下さい.