我的最后一个循环去了哪里?

时间:2012-08-06 05:52:31

标签: perl

在更新一些遗留的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!

测试结果似乎表明浮点表示不是问题所在。

这里发生了什么?

4 个答案:

答案 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,所以最后一个值超出范围。