Simple Perl For-Loop

孤街浪徒 提交于 2020-01-05 09:12:04

问题


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.


回答1:


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



回答2:


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



回答3:


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.




回答4:


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'


来源:https://stackoverflow.com/questions/19666091/simple-perl-for-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!