goichi has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I met strange behavior in perl (5.6.1 and below).
This simple script,
for (my $i = 2 ; $i >= 0.1 ; $i -= 0.1){ print $i, "\n"; }
was printed as follows.
2 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 0.999999999999999 0.899999999999999 0.799999999999999 0.699999999999999 0.599999999999999 0.499999999999999 0.399999999999999 0.299999999999999 0.199999999999999
Is this feature ? or bug?
If this is feature, where is document about it?
If bug, How should I do?

thanks.

Replies are listed 'Best First'.
Re: Reduce from 2 to 0.1.
by jeroenes (Priest) on Apr 10, 2001 at 19:13 UTC
    It's all about floating point approximation. If you work with floating points, you sometimes get bitten. I was. See Comparison misses a true in *some* cases for example.

    Solution: round it off with sprintf.

    Hope this helps,

    Jeroen
    "We are not alone"(FZ)
    Update: davorg is right. Stick with integers, or put the sprintf in the for:  $i = sprintf('%.1f', $i - 0.1);

      Solution: round it off with sprintf.

      Not actually a very good solution as this test shows:

      #!/usr/bin/perl -w use strict; for (my $i = 2 ; $i >= 0.1 ; $i -= 0.1){ printf "%.1f\n", $i; }

      The output is:

      2.0 1.9 1.8 1.7 1.6 1.5 1.4 1.3 1.2 1.1 1.0 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2

      Note that 0.1 is not printed, contrary to what you'd expect if you were reading the condition in the code.

      You'd probably need to adjust the condition to take this into account.

      --
      <http://www.dave.org.uk>

      "Perl makes the fun jobs fun
      and the boring jobs bearable" - me

Re: Reduce from 2 to 0.1.
by mirod (Canon) on Apr 10, 2001 at 19:40 UTC

    Stick to using integers if you can:

    for (my $i = 20 ; $i >= 1 ; $i -= 1){ print $i/10, "\n";

    or even:

    foreach my $i (reverse (1..20)){ print $i/10, "\n";
Re: Reduce from 2 to 0.1.
by jink (Scribe) on Apr 10, 2001 at 19:43 UTC
    How about this then?
    #!/usr/local/bin/perl5 -w use strict; for (my $i = 20 ; $i >= 1 ; $i -= 1){ printf "%f\n", $i/10; }
    It produces nice output:
    2.000000 1.900000 1.800000 1.700000 1.600000 1.500000 1.400000 1.300000 1.200000 1.100000 1.000000 0.900000 0.800000 0.700000 0.600000 0.500000 0.400000 0.300000 0.200000 0.100000
    But it's a rather horrible solution, really... :-)
      Whenever I needed decimals, to a specific number of places (and not a variable number of places), then I used an int scaled up the appropriate amount,then used a printf("%d.%d",i/100,i%100); (or whatever the scale was) to print.

      This avoided FPU use (way back when when FP was really, really slow), and avoided rounding errors.

      Of course, if you have a variable amount of decimal places, this might not be feasible.

      -lsd