代码:
<?php
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while ($i < $stop) {
echo($i . "<br/>");
$i += $step;
}
?>
输出:
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1 <-- notice the 1 printed when it shouldn't
创建了fiddle
还有一项:如果您设置$start = 1
和$stop = 2
,则可以正常使用。
使用:php 5.3.27
为什么要打印1
?
答案 0 :(得分:53)
因为不仅浮动数学有缺陷,有时它的表示形式is flawed too - 这就是这里的情况。
你实际上并没有获得0.1,0.2,...... - 这很容易检查:
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while ($i < $stop) {
print(number_format($i, 32) . "<br />");
$i += $step;
}
正如您所见,唯一的区别是echo
已被number_format
电话取代。但结果却截然不同:
0.10000000000000000555111512312578
0.20000000000000001110223024625157
0.30000000000000004440892098500626
0.40000000000000002220446049250313
0.50000000000000000000000000000000
0.59999999999999997779553950749687
0.69999999999999995559107901499374
0.79999999999999993338661852249061
0.89999999999999991118215802998748
0.99999999999999988897769753748435
请参阅?实际上只有0.5
一次 - 因为该数字可以存储在一个浮动容器中。所有其他只是近似值。
如何解决这个问题?好吧,一种激进的方法是使用不浮点数,而是使用类似情况下的整数。很容易注意到你这样做了......
$start = 0;
$stop = 10;
$step = (int)(($stop - $start) / 10);
$i = $start + $step;
while ($i < $stop) {
print(number_format($i, 32) . "<br />");
$i += $step;
}
......它可以正常工作:
或者,您可以使用number_format
将float转换为某个字符串,然后将此字符串与预格式化的float进行比较。像这样:
$start = 0;
$stop = 1;
$step = ($stop - $start) / 10;
$i = $start + $step;
while (number_format($i, 1) !== number_format($stop, 1)) {
print(number_format($i, 32) . "\n");
$i += $step;
}
答案 1 :(得分:13)
问题是变量$ i中的数字不是1(打印时)。它的实际值只有不到1.因此在测试中($ i&lt; $ stop)为真,数字转换为十进制(导致舍入为1),并显示。
现在为什么$ i不是1?这是因为你通过说10 * 0.1到达那里,并且0.1不能完美地用二进制表示。只能表示为有限次幂2之和的数字才能完美表示。
为什么然后$停止正好1?因为它不是浮点格式。换句话说,它从一开始就是精确的 - 它不是在使用浮点10 * 0.1的系统内计算的。
在数学上,我们可以写如下:
64位二进制浮点数只能保存总和的前27个非零项,大约为0.1。有效数的其他26位保持为零以指示零项。 0.1在物理上无法表示的原因是所需的术语序列是无限的。另一方面,像1这样的数字只需要很少的有限数量的项并且是可表示的。我们希望所有数字都是如此。这就是十进制浮点是如此重要的创新(尚未广泛使用)的原因。它可以代表我们可以写下的任何数字,并且完美地完成。当然,可用数字的数量仍然是有限的。
回到给定的问题,因为0.1是循环变量的增量并且实际上不可表示,所以在循环中永远不会精确地达到值1.0(尽管可表示)。
答案 2 :(得分:2)
如果您的步数始终为10,您可以使用以下方法快速完成此操作:
<?php
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while (round($i, 1) < $stop) { //Added round() to the while statement
echo($i . "<br/>");
$i += $step;
}
?>