This is a simple program but I am unable to understand logic/work being done by while loop behind the scene.
Problem: Write a program that prints every number from 0 to 1 that has a single digit after the decimal place (that is, 0.1, 0.2, and so on).
So here is my code:
$num = 0;
while ( $num < 1 ) {
print "$num \n";
$num = $num + 0.1;
}
If I write it in this way, it is going to print
$num = 0;
while ( $num < 1 ) {
$num = $num + 0.1;
print "$num \n";
}
Output:
0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1
Ideally speaking, 1 and 1.1 should not get printed in both code samples respectively. After printing 0.9 when 0.1 is added to it, it becomes 1.0 i.e. while (1.0 < 1). Hence condition in while loop is false, so it should not print 1 and 1.1. But that is what is happening.
Can someone please explain why while loop is working in this unexpected way i.e. printing 1 and 1.1 even when condition is false.
1/10 is a periodic number in binary just like 1/3 is periodic in decimal.
____
1/10 = 0.00011 base 2
As such, it can't be represented exactly by a floating-point number.
$ perl -e'printf "%$.20e\n", 0.1;'
1.00000000000000005551e-01
This imprecision is the cause of your problem.
$ perl -e'my $i = 0; while ($i < 1) { printf "%1\$.3f %1\$.20e\n", $i; $i += 0.1; }'
0.000 0.00000000000000000000e+00
0.100 1.00000000000000005551e-01
0.200 2.00000000000000011102e-01
0.300 3.00000000000000044409e-01
0.400 4.00000000000000022204e-01
0.500 5.00000000000000000000e-01
0.600 5.99999999999999977796e-01
0.700 6.99999999999999955591e-01
0.800 7.99999999999999933387e-01
0.900 8.99999999999999911182e-01
1.000 9.99999999999999888978e-01
Generally speaking, one can solve this by checking if the number is equal to another within some tolerance. But in this case, there's a simpler solution.
$ perl -e'for my $j (0..9) { my $i = $j/10; printf "%1\$.3f %1\$.20e\n", $i; }'
0.000 0.00000000000000000000e+00
0.100 1.00000000000000005551e-01
0.200 2.00000000000000011102e-01
0.300 2.99999999999999988898e-01
0.400 4.00000000000000022204e-01
0.500 5.00000000000000000000e-01
0.600 5.99999999999999977796e-01
0.700 6.99999999999999955591e-01
0.800 8.00000000000000044409e-01
0.900 9.00000000000000022204e-01
The above solution not only performs the correct number of iterations, it doesn't accumulate error, so $i is always as correct as it can be.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With