在更新一些遗留的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
确实大于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!
测试结果似乎表明浮点表示不是问题所在。
这里发生了什么?
答案 0 :(得分:5)
您会惊讶地发现大于2.5的值打印为2.5
?不要惊讶,这是正常的浮点数。普通打印不显示所有数字。试试这个:
printf "%.66g\n", $i
代替您的print
声明。在我的机器上,打印的最终值为2.5000000000000013
。你的可能会很相似。
答案 1 :(得分:1)
也许是因为浮动,它不仅仅是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
答案 2 :(得分:1)
i的值在不同时间增加。请注意,上次运行中的第一个值是0.9,而不是0.8。
第一个问题是浮点问题,在条件下尝试使用2.5001。尝试使用整数,8到25乘1,&lt; =工作正常。
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, $/;
}
答案 3 :(得分:1)
计算机使用二进制存储,而不是十进制!如果将十进制数0.1存储为二进制浮点数,则会产生一个infinte数字ob二进制数字:
dezimal 0.1 =二进制0.000110011001100110011001100110011 ...
因此,如果您不使用整数,则会出现舍入错误。 dezimal值0.1可能存储为数字编号0.0001100110011001101,这是dezimal 0.100006104
添加25次0.100006104给出2.50015259,这大于2.5,所以最后一个值超出范围。