为什么打印最后一个数字(1)?
代码:
<?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
创build了一个小提琴
另外:如果你设置$start = 1
和$stop = 2
它工作正常。
使用: php 5.3.27
为什么打印1
?
因为不仅浮法math有缺陷,有时它的表示也是有缺陷的 – 在这里就是这种情况。
你实际上不会得到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转换为某个string,然后将此string与预格式化的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; }
问题是variables$ i中的数字不是1(打印时)。 因此,在testing中($ i <$ stop)是真的,数字被转换成十进制(导致舍入为1),并显示出来。
现在为什么我不是1? 这是因为你通过说10 * 0.1来到那里,0.1不能用二进制完美表示。 只有可以表示为有限数目2的幂的和的数字才能被完美地表示。
为什么那么$是完全停止1? 因为它不是浮点格式。 换句话说,它从一开始就是确切的 – 它不会在系统内使用浮点数10 * 0.1来计算。
在math上,我们可以这样写:
一个64位的二进制浮点只能容纳总和近似0.1的前27个非零项。 有效数的其余26位保持为零以指示零项。 0.1的原因在物理上不能代表的是,所需的术语序列是无限的。 另一方面,像1这样的数字只需要有限数量的术语并且是可表示的。 我们希望这是所有数字的情况。 这就是为什么十进制浮点是如此重要的创新(尚未广泛使用)。 它可以代表我们可以写下的任何数字,并且完美地完成。 当然,可用数字的数量仍然是有限的。
回到给定的问题,因为0.1是循环variables的增量,并且实际上不是可表示的,所以在循环中从未精确地达到值1.0(尽pipe可表示)。
如果你的步骤总是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; } ?>