2

I need a loop to increase the number by 3rd decimal and the print should look like this

1.1
1.101
1.102
1.103
...
2.0
2.001
2.002
...
2.1
2.101
...

for ($i = 1.1; $i <= 3; $i += .001){

   echo $i . '<br />';
}

the start seems to be ok but somewhere along the loop the decimal number becomes to big

1.54
1.541
1.542
1.543
1.544
1.545
1.546
1.547
1.548
1.549
1.55
1.551
1.552
1.553
1.554
1.5549999999999
1.5559999999999
1.5569999999999
1.5579999999999
1.5589999999999

And if I use

number_format($i,3)

all my numbers have 3 decimal points like

1.100 and I need it to be 1.1 .

What am I missing ?

Benn
  • 4,840
  • 8
  • 65
  • 106
  • This has something to do with how floating point numbers are stored in PHP [link](http://php.net/manual/en/language.types.float.php) – Matej Žvan Mar 29 '15 at 18:38
  • 1
    @MatejŽvan That issue is not php specific, it is a general effect of floating point arithmetic. – arkascha Mar 29 '15 at 18:45

4 Answers4

2

Have a read about floating point numbers. Basically, not all decimal numbers can be precisely represented in floating points.

Try rounding the number:

for ($i = 1.1; $i <= 3; $i += .001){
   echo round($i,3) . '<br />';
}
button
  • 666
  • 6
  • 14
1

Live demo

Try rounding the values and store them in $i:

for ($i = 1.1; $i <= 3; $i += .001){
    $i = round($i, 3);
   echo $i . '<br />';
}
CMPS
  • 7,733
  • 4
  • 28
  • 53
1

This should work for you:

(Here I just use sprintf() and specify that it should display a double (lf -> long float) to the 3rd decimal point)

echo sprintf("%.3lf", $i) . '<br />';

If you wonder why you get this results see: Is floating point math broken?

BTW: This method also has the side effect, that it displays: 1.560 instead of 1.56

Community
  • 1
  • 1
Rizier123
  • 58,877
  • 16
  • 101
  • 156
1

This is an effect of how floating point arithmetic works. That can lead to poor precision. The issue is that the error in computation adds up. So rounding alone is not really safe in general. Its safety depends in the number of additions done, so on the number of iterations here...

Have a try like that instead:

for ($i = 1100; $i <= 3000; $i++) {
   echo $i/1000 . "<br>\n";
}

The above uses integer computation instead, the following division does not lead to added up errors.

arkascha
  • 41,620
  • 7
  • 58
  • 90
  • @button Sure it does, give it a try. Why shouldn't it? – arkascha Mar 29 '15 at 18:41
  • it is actually right on the money but I dont see why round would be different in this case? can you collaborate ? – Benn Mar 29 '15 at 18:50
  • As said: the error adds up with the iterations (OK, that is simplified, but still true). Just rounding the output is unsafe. What would work would be to round the sums, so something like `for ($i = 1.1; $i <= 3; $i = round($i+.001,3))`. Because the max error per iteration is guaranteed to be lower than the iteration. But that would be much slower... The above is easier and faster. – arkascha Mar 29 '15 at 18:51
  • @Benn Sorry, forgot to ping you. See above: ^^^ – arkascha Mar 29 '15 at 18:58
  • understand now , but for my use ( I use it for em line height ) the round seems to be ok and precise enough. if this was bigger iteration I would go with your solution. thank you for the info – Benn Mar 29 '15 at 19:08