02
1109

ループの終了条件に小数を指定するのは避けるべき

C/C++の話題。あんまり頭働いてない状態でコード書いてたら引っ掛かった!
なさけない話題なので書くの恥ずかしいんだけれど、教訓として残しておく。

int sum = 0;
for ( int i = 0; i < 10; i += 1 ) {
        cout << i << ", ";
        sum++;
}
cout << "num of loop execute: " << sum << endl;

たとえば上のようなコードだとループ内は10回実行されるのは当たり前なんだけれど、
下の場合はループ回数は不定となる(たぶん環境によりばらつきがある)。ぼくの環境では11回実行された。

int sum = 0;
for ( double i = 0.0; i < 1.0; i += 0.1 ) {
        cout << i << ", ";
        sum++;
}
cout << "num of loop execute: " << sum << endl;

他の例として、以下の例ではループが終了せず無限ループに入る。
ループ終了条件が != になってるだけです。

int sum = 0;
for ( double i = 0.0; i != 1.0; i += 0.1 ) {
        cout << i << ", ";
        sum++;
}
cout << "num of loop execute: " << sum << endl;

原因は、 11回目の実行時にdouble i が1.0であるにも関わらず i < 1.0 の終了条件を通ってしまうから。
3つ目の例での変数 i は、0.1ずつインクリメントしていっても(つまり0.0に10回0.1を足しても)厳密に「i == 1.0」にはならない可能性がある。

double, float等の浮動小数点は、完全に表現できない値がある。
たとえば0.1という数字は二進数にすると循環小数になり、そこで誤差が生じる。
まあ普通の計算では問題になりにくい程度の誤差だけれど、それをループ終了条件に用いた場合に誤差が効いてくる、ということ(だと思う)。

対策としてはループ終了条件を整数としてとれるように工夫する、ということくらいしか思いつかないのだけれど…
どこかで一度目にした教訓だったと思うのだけれど、実際に遭遇するとなかなか時間とられるなあ。
無限ループって怖くね?

posted on 2009-11-02 20:50 | Tags :