3

一部のレガシー 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 !

そして、サービスが再開されました(と私は思った)。


検証

$ifinalが実際に 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!

テスト結果は、ここでは浮動小数点表現が問題ではないことを示しているようです。

ここで何が起こっているのですか?

4

4 に答える 4

5

2.5より大きい値が2.5?として出力されることに驚いています。驚かないでください、それは通常の浮動小数点のものです。プレーンプリントでは、すべての数字が表示されるわけではありません。これを試して:

printf "%.66g\n", $i

あなたのprint声明の代わりに。私のマシンでは、印刷される最終的な値は2.5000000000000013です。あなたはおそらく似ているでしょう。

于 2012-08-06T06:16:32.800 に答える
1

多分それはfloatが原因で、2.5だけではなく、次のように使用できます。

for ($i = 0.8; $i < 2.6; $i += 0.1) {

またはあなたが見ることができます:

for ($i = 0.8; $i <= 2.5; $i += 0.1) {
    print $i, "\n";
}
print $i, "\n";
print $i <= 2.5 ? 'TRUE' : 'FALSE';

出力:

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
FALSE
于 2012-08-06T05:58:52.730 に答える
1

i の値は、さまざまな時点で増加しています。前回の実行の最初の値が 0.8 ではなく 0.9 であることに注意してください。

最初の問題は浮動小数点です。条件に 2.5001 を使用してみてください。8 ~ 25 x 1 の整数を使用してみてください。<= は正常に機能します。

for($i = 8; $i <= 25; $i += 1 ) {
    print $i/10, "\n";
}

または

  print $_/10, "\n" for 8..25;

また

my $epsilon = .00000001;

for(my $i = 0.8; ($i - 2.5) <= $epsilon; $i += .1 ) {
    print $i, $/;
}
于 2012-08-06T05:59:05.823 に答える
1

コンピュータは、 10 進数ではなく2 進数のストレージを使用します。10 進数 0.1 を 2 進浮動小数点数として格納すると、2 進数の無限数になります。

dezimal 0.1 = バイナリ 0.000110011001100110011001100110011...

したがって、整数を使用しないと、丸め誤差が発生します。10 進値 0.1 は、デジタル数 0.0001100110011001101 として格納される場合があり、これは 10 進値 0.100006104 です。

0.100006104 を 25 倍すると 2.50015259 になり、これは 2.5 を超えているため、この最後の値は範囲外です。

于 2012-08-06T06:19:30.700 に答える