« 『Motoya Espresso Express』に行ってみた♪ | トップページ | 風邪には『イソジンうがい薬』! »

2009年11月22日 (日)

多少の誤差を無視させ同じ値と判定させるには!?

処理を作成した時に、どんな入力値に対しても
正しく動作しているかを調べるのに苦労する。

入力値を変えながら100回ぐらい結果を出してみる。
その数値を眺めても結果が正しいかなんてわからん!

処理が単純ならエクセルなんかで検算するのも手だ。
図にするのも良いだろう。

だが演算が複雑で入力する値が多い場合や
数百万回の動作確認を行いたい場合には、
別なアプローチで作成した処理の結果と比較するのが
有効な手段だろう。

同じ値を入力すれば、同じ結果になる。

違った場合には、どっちかの処理がバグっている訳だ。

二つの処理を作らなくてはならないが
バグの発見がグッと楽になる。

100万回ぐらいの試行錯誤もPC任せで出来る。
出力結果が異なった部分だけ抜き出して検証すれば良い訳だ。

しかし、処理中の数値の変換過程やらなんやらで
出力結果が微妙に違ったりする!

まあ、誤差と言う奴でしょうか…。

整数(『int』)なら大抵これで終わりなはずだ。

if(a==b){/* 同じ値だ!*/}

だが、浮動小数点(『float/double』で同じ物かを判断する場合、
大抵こんな風に小数点以下を切って比較するだろう。

if((int)a==(int)b){/*同じ値だ!*/}
if((int)(a*100)==(int)(b*100)){/*同じ値だ!*/}

上記は、『a/b』の有効桁が固定している場合には良いのだが
出力結果が30~-30桁なんて幅のある数値だと
上記のような処理では、対応できない…。

で、どうしたら良い物か考えた…

ハッ!と閃いた!

元の値と差分の値の桁数を比較すればOKじゃねぇ?

基本の考え方は、以下の通り。

1.同じものなら差分は、0
2.ほぼ同じものなら桁数は同じ
3.ほぼ同じものなら差分は元の桁数より桁違いに小さい

『3』の『桁違いに小さい』がポイントね!

ちなみに-7桁とかの小さ過ぎる数値は、0とみなす。
0に限り無く近いと誤差が大きくなり過ぎて比較できないので。

で、桁数で同じかどうか判定する処理を作ってみた。

#include    <windows.h>
#include    <stdio.h>
#include    <math.h>
#define        BORDER_ZERO        -7    // ゼロとみなす桁数
#define        BORDER_DIFFER    -4    // 誤差として切り捨てる桁数
BOOL PlaceComp( double a, double b )
{
    double    c, la, lb, lc;

    c    = a - b;
    if( c == 0 )
        return TRUE;    // 同じ物です。
    la    = a == 0 ? BORDER_ZERO : log10( fabs( a ) );
    lb    = b == 0 ? BORDER_ZERO : log10( fabs( b ) );
    lc    = c == 0 ? BORDER_ZERO : log10( fabs( c ) );
    if( ( la <= BORDER_ZERO ) && ( lb <= BORDER_ZERO ) )
        return TRUE;    // 小さすぎる値は、ゼロとみなします。
    if( ( int )la != ( int )lb )
        return FALSE;    // 桁数が異なる場合、明らかに違う値です。
    if( ( int )lc <= ( ( int )la + BORDER_DIFFER ) )
        return TRUE;    // 差分の桁数が十分小さいなら同じものとします。
    return FALSE;
}
void main( void )
{
    double    table[][2]    =
    {
        {    -2.2494911121205649e-031,    7.4983033152370995e-032,    },
        {    3.8268344402313232,            3.8268344402313232,            },
        {    1.0800063947812803e-015,    1.0800063947812803e-015,    },
        {    -9.4531962702149031e-008,    0.00000000000000000,        },
        {    6.2361249923706055,            6.2361254692077637,            },
        {    2.7059803009033203e21,        2.7059805393218994e21,        },
        {    3.1259803009033203e21,        3.1269805393218994e21,        },
    };
    int        i, f;
    for( i = 0 ; i < sizeof( table ) / sizeof( *table ) ; i++ )
    {
        f    = PlaceComp( table[i][0], table[i][1] );
        printf( f ? "same\n" : "differ\n" );
    }
}

結果
same
same
same
same
differ
same
differ

『BORDER_DIFFER』を『-6』にすると『differ』が一つ減る。
『-3』にすると全て『same』になる。

誤差がどの程度かで『define』の設定値を合わせて変えればOKなはず。

実際、必要に迫られたので作ってみたけど結構使えるかも…。

取り敢えずメモとして書いておこう…。

|

« 『Motoya Espresso Express』に行ってみた♪ | トップページ | 風邪には『イソジンうがい薬』! »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: 多少の誤差を無視させ同じ値と判定させるには!?:

» エクセル テンプレート [エクセル テンプレート]
エクセル テンプレートについての情報です。 [続きを読む]

受信: 2009年11月27日 (金) 23時28分

« 『Motoya Espresso Express』に行ってみた♪ | トップページ | 風邪には『イソジンうがい薬』! »