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 :

06
1109

私もうfloatくんのこと…信じられない…!

ざわ…ざわ…

#include <stdio .h>

int main() {
        float x = 16777216.0;
        printf( "x = %f\n", x );
        x += 1.0;
        printf( "x+1.0 = %f\n", x );

        x = 16777217.0;
        printf( "16777217.0 = %f\n", x );

        return 0;
}
出力:
x = 16777216.000000
x+1.0 = 16777216.000000
16777217.0 = 16777216.000000

16777217 = 16777216
そうかわかった人類は滅亡する!

浮動小数は基本的に数が大きくなればなるほど精度(表せる数の幅)が大きくなるとか、16777215で仮数部が最大値となって指数部が繰り上がることによって数字の最小単位(分解能みたいなもの?)が2になるとか、ごにょごにょといろいろあるらしいのだけれど、解説はまた気が向いたら。ぼくに理解可能ならば。

posted on 2009-11-06 06:15 | Tags :

09
1109

TeXで複数行をコメントアウトする

メモメモ。
texでは % のあとの文字は出力されてないことを利用して、行頭に%を付けてコメントを残すことが慣習的に行われる。

図を一旦消したいとかいう状況には複数行のコメントアウトができた方が便利なんだけれど、そういうときは
texファイルのはじめの方で

?usepackage{comment}

とやっておいて、コメントアウトしたい領域を

?begin{comment}
?end{comment}

で挟んでやるといい。

posted on 2009-11-09 00:01 | Tags :