一部のレガシー Perl コードを更新する過程で、C スタイルのfor
ループが途中で終了する状況に遭遇しました。
問題
状況は次のように再現できます (Linux と Windows で同じ結果が得られます)。
$ perl
for($i = 0.8; $i <= 2.5; $i+=0.1){
print $i, "\n";
}
__END__
0.8
0.9
1
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
2
2.1
2.2
2.3
2.4
2.5 は著しく欠けています。条件を通過する必要がありました$i <= 2.5
。
修正
で説明したように、浮動小数点表現が原因である可能性があることに気付きperldoc perlfaq4
、条件を次のように更新しました。
$ perl
for($i = 0.8; sprintf( '%.1f', $i ) <= 2.5; $i += 0.1 ) {
print $i, "\n";
}
__END__
0.8
0.9
1
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
2
2.1
2.2
2.3
2.4
2.5 <--- Gotcha !
そして、サービスが再開されました(と私は思った)。
検証
$i
finalが実際に 2.5 より大きいことを証明したかったので、インクリメントをdo
ループでラップしました。
$ perl
for($i = 0.8; $i <= 2.5; do { $i += 0.1; print $i, "\n"; } ) {}
__END__
0.9
1
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
2
2.1
2.2
2.3
2.4
2.5 <--- Surprise!
テスト結果は、ここでは浮動小数点表現が問題ではないことを示しているようです。
ここで何が起こっているのですか?