3

When I run:

for($o=1;$o<=655;$o++){ $r = $r+0.01; echo $r." ";}

at some point I get:

...4.29 4.3 4.31 4.32 4.33 4.34 4.35 4.36 4.37 4.38 4.39 4.4
4.41 4.42 4.4299999999999 4.4399999999999 4.4499999999999
4.4599999999999 4.4699999999999 ...

But when I run:

for($o=1;$o<=5;$o+=0.01){ echo $o." "; }

anomaly starts at:

4.34 4.35 4.36 4.37 4.38 4.3899999999999 4.3999999999999

Why is there a difference between the addition being part of the for loop, or within the for loop?

FThompson
  • 28,352
  • 13
  • 60
  • 93
meso_2600
  • 1,940
  • 5
  • 25
  • 50
  • Standard floating point inaccuracies (google it, or read the following thread: http://stackoverflow.com/questions/2100490/floating-point-inaccuracy-examples) – ChristopheD Nov 07 '12 at 23:17
  • 1
    http://php.net/language.types.float - see the big red box - [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – hakre Nov 07 '12 at 23:32
  • I am fully aware of that, but why does it make difference when it's defined within for loop and after it? If I use $o++ and $r = $r+0.01 it puts different results to when I use $o+=0.01. This is the actual question – meso_2600 Nov 08 '12 at 00:37
  • 1
    I am guessing that $r starts with the value 0. Then it has .01 added to it 443 times, at which point the accumulated rounding errors (in converting “.01” to floating point and in adding the converted value to a sum) cause enough of a difference that the output formatter decides to print more digits rather than rounding to 4.43. On the other hand, $o starts at 1 and does not experience the rounding errors incurred in the first 100 adds to $r (as it progresses from 0 to about 1). When $r reaches 1, it is actually 1.0000000000000006661338147750939242541790008544921875, so it differs from $o. – Eric Postpischil Nov 08 '12 at 00:51

4 Answers4

5

Welcome to the wonders of rounding errors.

0.01 is not exactly expressible as a finite binary fraction, so you'll pick up rounding errors eventually if you add it repeatedly to another floating point number.

The specific point at which you see the rounding error depends on how the binary expansion of the numbers involved play out to produce the rounding error.

John
  • 15,990
  • 10
  • 70
  • 110
  • I am fully aware of that, but why does it make difference when it's defined within for loop and after it? – meso_2600 Nov 08 '12 at 00:17
  • I don't know when you set the value of `$r`, or what you set it. You use it in the loop, but you don't post the code that sets the initial value. That could be the difference there. It may have nothing to do with the loop. – John Nov 08 '12 at 00:28
  • that's the actuall place I set the value, before that there is no other init of the $r or $o – meso_2600 Nov 08 '12 at 00:37
1

Floating point math can't represent the values exactly. See here.

Telgin
  • 1,614
  • 10
  • 10
1

When the numeral “.01” is converted to 64-bit IEEE 754 binary floating point, the result is 0.010000000000000000208166817117216851329430937767028809. When $r is set to 0, and this approximation of .01 is added to it 100 times, the result is 1.0000000000000006661338147750939242541790008544921875, due to additional rounding errors during the additions.

Now consider that $r is further incremented from this value slightly over 1, while $o is incremented from its starting value of exactly 1. First, since $r and $o start with different values, they will generally have different values later. Second, because they are different, as the increment is added to each of them, they encounter different errors from rounding in the additions. This errors can happen to reinforce or to cancel, causing $r or $o to be more or less different.

Finally, PHP has some criterion for deciding whether to print a number with two decimal points or more. Presumably, at some point the value of $r or of $o is sufficiently far away from the nearest numeral with two decimal points that PHP decides it should print more digits. As explained above, this happens at different times for $r and $o.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

You can use the Round() function. Here is a link to it: http://php.net/manual/en/function.round.php