The code:
<?php
$start = 0;
$stop = 1;
$step = ($stop - $start)/10;
$i = $start + $step;
while ($i < $stop) {
echo($i . "<br/>");
$i += $step;
}
?>
The output:
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
Created a fiddle
One more: if you set $start = 1
and $stop = 2
it works fine.
Using: php 5.3.27
Why is the 1
printed?
If your step will always be a factor of 10, you can accomplish this quickly with the following:
Because not only float math is flawed, sometimes its representation is flawed too - and that's the case here.
You don't actually get 0.1, 0.2, ... - and that's quite easy to check:
The only difference here, as you see, is that
echo
replaced withnumber_format
call. But the results are drastically different:See? Only one time it was
0.5
actually - because that number can be stored in a float container. All the others were only approximations.How to solve this? Well, one radical approach is using not floats, but integers in similar situations. It's easy to notice that have you done it this way...
... it would work ok:
Alternatively, you can use
number_format
to convert the float into some string, then compare this string with preformatted float. Like this:The problem is that the number in the variable $i is not 1 (when printed). Its actual just less than 1. So in the test ($i < $stop) is true, the number is converted to decimal (causing rounding to 1), and displayed.
Now why is $i not 1 exactly? It is because you got there by saying 10 * 0.1, and 0.1 cannot be represented perfectly in binary. Only numbers which can be expressed as a sum of a finite number of powers of 2 can be perfectly represented.
Why then is $stop exactly 1? Because it isn't in floating point format. In other words, it is exact from the start -- it isn't calculated within the system used floating point 10 * 0.1.
Mathematically, we can write this as follows:
A 64 bit binary float can only hold the first 27 non-zero terms of the sum which approximates 0.1. The other 26 bits of the significand remain zero to indicate zero terms. The reason 0.1 isn't physically representable is that the required sequence of terms is infinite. On the other hand, numbers like 1 require only a small finite number of terms and are representable. We'd like that to be the case for all numbers. This is why decimal floating point is such an important innovation (not widely available yet). It can represent any number that we can write down, and do so perfectly. Of course, the number of available digits remains finite.
Returning to the given problem, since 0.1 is the increment for the loop variable and isn't actually representable, the value 1.0 (although representable) is never precisely reached in the loop.