Rambatino
Rambatino

Reputation: 4914

Simple Perl For-Loop

I have a for loop and I want to increment the variable by 0.1 each time, however the value changes differently from the increment and I am unsure as to why.

I've simplified the for loop and it still gives a strange output:

for (my $t = 1000; $t < 1500 ;$t+=0.1) {
print "$t\n";
}

It prints:

1034.9
1035
1035.1
1035.2
1035.3
1035.4
1035.49999999999
1035.59999999999
1035.69999999999
1035.79999999999
1035.89999999999
1035.99999999999
1036.09999999999
1036.19999999999
1036.29999999999
[it then goes on like this to 1500]

I do not know where the decimal places are coming from. Is this a problem with my understanding of Perl?

Thanks in advance.

Upvotes: 1

Views: 4261

Answers (4)

ikegami
ikegami

Reputation: 386386

1/10 is a periodic number in binary like 1/3 is in decimal. It cannot be represented exactly as a floating point number.

$ perl -e'printf "%.20g\n", 0.1'
0.10000000000000001

Never compare a floating pointer number to another without involving a tolerance, and be wary of accumulation of error.

The simple solution here to to do the arithmetic using integers, and generate the floating point numbers when needed

for (my $tx10 = 10000; $tx10 < 15000; ++$tx10) {
   my $t = $tx10/10;
   print "$t\n";
}

which simplifies to

for my $tx10 (10000..14999) {
   my $t = $tx10/10;
   print "$t\n";
}

         ____            ____             ____
0.1 = 0.00011    0.4 = 0.0110    0.7 = 0.10110
        ____                              ____
0.2 = 0.0011     0.5 = 0.1       0.8 = 0.11001
         ____            ____             ____
0.3 = 0.01001    0.6 = 0.1001    0.9 = 0.11100

Upvotes: 9

runrig
runrig

Reputation: 6524

Alternative:

for (10000..14999) {
  my $t = $_/10;
  print "$t\n";
}

Since 0.1 cannot be exactly specified in binary, rounding errors will accumulate in your code. In this answer, the amount always stays close enough to exact so that perl's internal number to string rounding will display the correct number. Lesson: use integers whenever possible.

Upvotes: 1

Vorsprung
Vorsprung

Reputation: 34377

To test for the condition

perl -le 'for (my $t = 1000.0; $t < 1500.0 ;$t+=0.1) {  print $t}'| perl -n -e '($a)=/(\d$)/; print "fail $a $b $_" if ($a eq $b); $b=$a'

yet another way to fix it

perl -le 'for (my $t = 1000.0; $t < 1500.0 ;$t+=0.1) { $t=sprintf("%.1f", $t); print $t}'| perl -n -e '($a)=/(\d$)/; print "fail $a $b $_" if ($a eq $b); $b=$a'

Upvotes: 0

Benjamin
Benjamin

Reputation: 101

for (my $t = 1000; $t < 1500 ;$t+=.1) {
    printf("%.1f\n", $t);
}

Upvotes: 2

Related Questions