概要
概要
qlib の使用上の諸注意や補足について述べます.
目次
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 の数) |
ソースの中の定数,例えば 3.0 は double,3.0L は long double の精度しかありません. 定数の値が 2^n (2 の n 乗) の場合,または倍精度で「きれいに」表現できる数 (比較的桁の小さな整数) ならば良いのですが,そうでない場合には単に定数を代入したのでは精度が落ちます.
定数の精度を落とさないためには,次のような方策があります.
その定数を頻繁に使うなら,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");
}
}
qlib は c から使う環境よりも c++ から使う環境の方が完成度が少しだけ低いです. しかし,c にて実現されていることは容易に c++ でも実現できます. 以下に示すような要領で補完して使いましょう.
qfloat.h は qlib に存在する算術関数すべてを反映しているわけではありません. (例えば 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 を置きましたので,ご活用下さい.