这是一个简单的程序,但我无法理解while loop在幕后所做的逻辑/工作。
问题:编写一个程序,打印从0到1的每个数字,小数点后有一个位数(即0.1、0.2等)。
这是我的代码:
$num = 0;
while ( $num < 1 ) {
print "$num \n";
$num = $num + 0.1;
}
如果我这样写它,它就会打印出来
$num = 0;
while ( $num < 1 ) {
$num = $num + 0.1;
print "$num \n";
}
输出:
0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1
理想地说,1和1.1不应该分别打印在两个代码示例中。打印0.9后,当0.1添加到它时,它变成1.0,即,而(1.0
有人能解释一下为什么while循环会以这种意想不到的方式工作,即即使条件为false,也会打印1和1.1。
考虑一下:
use strict;
use warnings;
use v5.10;
my $num = 0;
while ($num < 1) {
say $num, " (", $num-1, ")";
$num += 0.1;
}
这一产出:
0 (-1)
0.1 (-0.9)
0.2 (-0.8)
0.3 (-0.7)
0.4 (-0.6)
0.5 (-0.5)
0.6 (-0.4)
0.7 (-0.3)
0.8 (-0.2)
0.9 (-0.1)
1 (-1.11022302462516e-16)
正如您所看到的,由于浮点精度问题,通过重复添加0.1获得的数字不完全是一,而是略小于一,这就是为什么检查比预期多成功一次的原因。
现在将此添加到脚本顶部的导入中:
use bignum;
并观察输出的变化:
0 (-1)
0.1 (-0.9)
0.2 (-0.8)
0.3 (-0.7)
0.4 (-0.6)
0.5 (-0.5)
0.6 (-0.4)
0.7 (-0.3)
0.8 (-0.2)
0.9 (-0.1)
这是因为现在的计算是在高精度模式下进行的,浮点错误不再悄悄出现。
通常,人们会被浮点运算咬上几次,然后学会用整数或ε来完成重要的部分。谷歌,有很多关于如何处理浮点精度错误的资源。
在第二个版本中,在循环检查之前打印,即它甚至会打印已经超出循环条件的案例。
得到1.0和1.1的原因是,由于浮点精度的原因,1.0略高于添加大量0.1的结果。
所以循环实际看到的是
所以为了解决,使用第一个版本,但从0.1开始,对照0.95进行检查。
$num = 0.1;
while ( $num < 0.95 ) {
print "$num \n";
$num = $num + 0.1;
}
1/10在二进制中是一个周期数,就像1/3在十进制中是周期数一样。
____
1/10 = 0.00011 base 2
因此,它不能用浮点数精确表示。
$ perl -e'printf "%$.20e\n", 0.1;'
1.00000000000000005551e-01
这种不精确是你问题的原因。
$ perl -e'my $i = 0; while ($i < 1) { printf "%1\$.3f %1\$.20e\n", $i; $i += 0.1; }'
0.000 0.00000000000000000000e+00
0.100 1.00000000000000005551e-01
0.200 2.00000000000000011102e-01
0.300 3.00000000000000044409e-01
0.400 4.00000000000000022204e-01
0.500 5.00000000000000000000e-01
0.600 5.99999999999999977796e-01
0.700 6.99999999999999955591e-01
0.800 7.99999999999999933387e-01
0.900 8.99999999999999911182e-01
1.000 9.99999999999999888978e-01
一般来说,可以通过检查数字是否在某个公差范围内等于另一个来解决此问题。但在这种情况下,有一个更简单的解决方案。
$ perl -e'for my $j (0..9) { my $i = $j/10; printf "%1\$.3f %1\$.20e\n", $i; }'
0.000 0.00000000000000000000e+00
0.100 1.00000000000000005551e-01
0.200 2.00000000000000011102e-01
0.300 2.99999999999999988898e-01
0.400 4.00000000000000022204e-01
0.500 5.00000000000000000000e-01
0.600 5.99999999999999977796e-01
0.700 6.99999999999999955591e-01
0.800 8.00000000000000044409e-01
0.900 9.00000000000000022204e-01
上面的解决方案不仅执行正确的迭代次数,而且不会累积错误,所以$i
总是尽可能正确。
Perl 循环 while 语句在给定条件为 true 时,重复执行语句或语句组。循环主体执行之前会先测试条件。 语法 语法格式如下所示: while(condition) { statement(s); } 在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。 condition 可以是任意的表达式,当条件为 true 时执行循环。 当条件为 fals
Perl 循环 不像 for 和 while 循环,它们是在循环头部测试循环条件。在 Perl 语言中,do...while 循环是在循环的尾部检查它的条件。 do...while 循环与 while 循环类似,但是 do...while 循环会确保至少执行一次循环。 语法 语法格式如下所示: do { statement(s); }while( condition ); 请注意,条件表
我有一个用户表和一个高尔夫比赛分数表。当用户参加比赛时,他使用表格在结果表中记录分数。我想显示一个结果表,显示用户的完整列表和比赛的分数。表中有八列分数——每门课一列。我正在努力使用php代码来显示结果分数。如果一名球员已经比赛,他的得分将正确显示,但如果表中的下一名球员没有比赛,则他的得分将显示为表中高于他的球员的得分。这将在列表中继续下去,直到获得真正的分数。我试图找到答案,但没有成功。这是我
我正在尝试制作一个简单的程序,它使用扫描器进行输入,并有一个while循环,该循环继续输入,直到输入结束字符为止。我想让扫描器接受输入并将一个字符串添加到堆栈列表中。我试图弄清楚为什么这段代码在键入空格时不终止while循环。
主要内容:循环控制语句,无限循环,实例有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。 编程语言提供了更为复杂执行路径的多种控制结构。 循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图: 注意,数字 0, 字符串 '0' 、 "" , 空 list () , 和 undef 为 false ,其他值均为 true。 tru
有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。 编程语言提供了更为复杂执行路径的多种控制结构。 循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图: 注意,数字 0, 字符串 '0' 、 "" , 空 list () , 和 undef 为 false ,其他值均为 true。 true