Reputation: 4914
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
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
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
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