アトリエ ぺっぺ

トップページ > プログラムTips > 実数の比較

◆ 実数の比較
プログラムを組む上で、実数の比較をするときには注意が必要です。
 
例えばある2個の整数が同じかどうか比較する場合、C言語では以下のように書きますね。
    int a = 5;
    int b = 4;
    bool bSame = ( a == b );
 
しかしこれを実数でも同じように書くと、ある問題が起こります。
例えば、次のような比較をプログラムで試してみてください。
    double d = 100.0 * ( 1.0 / 3.0 ) * 3.0;
    if(d == 100.0){
        printf("100.0 * ( 1.0 / 3.0 ) * 3.0は、100.0です。");
    }
    else{
        printf("100.0 * ( 1.0 / 3.0 ) * 3.0は、100.0ではありません・・なぜ?");
    }
100に3分の1をかけて、それをまた3でかけても、100に戻らない・・?
 
ご存知のように、100に3分の1をかけると、その答えは33.3333333333333333333333333333........というように、永遠に3が並ぶ値になりますね。
しかし、『永遠に3が並ぶ』という値を、限りあるメモリに覚えさせることはできません。
何せ永遠ですから。
 
なので、コンピュータでこのような値を処理するときは、適当な場所で切り捨ててしまいます。
33.3333333333333333333333333、この辺りでいいか、というような具合です。
これをまた3倍したからといっても、でてくるのは下記の値です。
 
99.9999999999999999999999999
 
確かにこれは、100.0という値とは同じではありませんね。
このように、コンピュータ上での実数の計算には、計算の過程で生じてしまう『誤差』が存在します。
 
この誤差をどのように扱うかは、その状況により違うと思いますが、一番簡単なのは「このくらいの違いなら同じとみなす」という『許容値』を作ってしまう方法です。
 
例えば、0.0001以下の違いしかなければ、同じ値とみなしてしまう、ということです。
これを言い換えると、次のようになります。
 
● 実数aとbがある場合、もしa - bの結果の絶対値(※1)が、0.0001より小さければ、aとbは同じとみなす
 
関数にすると、こんなかんじです。
    bool EQ ( double d1, double d2 ){
        return ( fabs ( d1 - d2 ) < 0.0001 );
    }
fabs()は、実数の絶対値を返す関数です。
d1とd2の差を算出し、その絶対値が0.0001より小さければtrue、大きければfalseを返します。
 
※1
絶対値とは、数字からマイナス符号を取ってしまったものです。
数字自体の大きさをあらわします。
5の絶対値は5、−5の絶対値も5ということになります。
 
上の例では、差の大きさだけ知りたいのであって、その差が正でも負でも関係ないため、絶対値を取っている、ということですね。
 
 
さて、イコールの比較に許容値を組み込んだ場合、大小の比較にも許容値を組み込まなければなりません。
なぜなら、一方では『同じ』と判定されているのに、一方では『小さい』と判定されてしまうからです。
    double d1 = 3.0000000001;
    double d2 = 3.0000000002;
    
    bool bEq = EQ(d1, d2);  // = true イコールが成り立つ
    bool bLt = d1 < d2;    // = true 小さい、も成り立つ カッフン
これでは、まともなプログラムは組めませんね。
なので、イコールの判定と同じように、大なり小なりにも許容値を組み込みます。
関数にすると、次のようになるでしょうか。
	// 大なりの判定(dXが、dValより大きいかどうか)
    bool GT( double dX, double dVal ){
        dX >= dVal + 0.0001;
    }
    
    // 小なりの判定(dXが、dValより小さいかどうか)
    bool LT( double dX, double dVal ){
        dX <= dVal - 0.0001;
    }
以上のように許容値を揃えることで、先ほどの判定でも、矛盾する結果がでることはなくなります。
    double d1 = 3.0000000001;
    double d2 = 3.0000000002;
    
    bool bEq = EQ(d1, d2);// = true イコールが成り立つ
    bool bLt = LT(d1, d2);// = false 小さい、は成り立たなくなる
同じように、以上・以下の判定用の関数を作ってもよいでしょう。
また、ここでは普通の関数を作りましたが、計算用のクラスのメソッドとして作ったり、マクロ関数で実装する方法もあります。
用途にそった実装方法を選んでください。
 

(C) 2002 atelier-peppe
ababa@atelier-peppe.sakura.ne.jp