CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: General Discussions: General Questions:
a dault

 



xiejun
New User

Nov 16, 2012, 5:00 PM

Post #1 of 4 (3590 views)
a dault Can't Post

I have found a strange question when I just write a small perl program.This is a code:
File Edit Options Buffers Tools Help
#!/usr/bin/perl
use warnings;use strict;

my $s = 0.81;
my ($i,$h,$n,$m)=(0,0,0,0);
while ($s <1.02){
$h =3 * $s * $s * $s ;
$n =2 * $s * $s;
$m = $h + $n;
$i++;
( print "$m\n";)
if ($m == 5){
print "$i,chenggong";
exit;
}
else{
$s += 0.01;
}
}
print "$i";

The program is simple.What is the question is that when I assign the $s as 0.82.The "if loop" can be run correctly as I expected,however,when I appoint the $s as 0.81,the program prints the last code ,this make me confused.With the code of $s++,combine 0.82 and 0.81,the "if" just need run one time again,I debug the program,and add the code in the bracket,and the result is the $m get the value 5 with the loop.
So what is the problem?Can you give me a help?Thank you

Code



      
    


Laurent_R
Enthusiast / Moderator

Nov 17, 2012, 5:38 AM

Post #2 of 4 (3576 views)
Re: [xiejun] a dault [In reply to] Can't Post

I do not understand your question, but I can tell you a couple of things about what your program does, which might actually answer your questions.

You set $s to 0.81. Then, each time through the loop, you increase it by 0.01. At one point (after 19 iterations), $s reaches 1. At this point, you have $h = 3 and $n = 2, so that $m is now 5. And it does print 5 in your example.

However, it is not exactly 5 (and it is also not exactly 3 and exactly 2). This is because floating point numbers are stored internally in floating point binary format, which cannot be (or can almost never be) an exact representation of the equivalent decimal number. Just as, in decimal numbers, 1/3 cannot be represented accurately as a decimal number: 0.33333333... If you had a computer storing internally numbers in decimal format, you would need to round off 1/3 before you could store it. This is exactly what happens here, some numbers cannot be stored accurately in the computer internal binary format and need to be round off.

When $h reaches 3, its value is not actually 3, but something extremely close to 3 (same for $n, not exactly 2). In fact, at this point, if you print $h, it will print 3; but if you print ($h - 3), you will not output 0, but something like 2.66453525910038e-15; similarly, ($n - 2) will print as 8.88178419700125e-16 (well, at least, that's what it does on my computer, you may get another result if your computer has a different architecture), and print ($m - 5) will output 3.5527136788005e-15, so, in this case, all three numbers happen to be very slightly larger than the integers you may think they are (with another example, they could have been slightly smaller, or one number larger and one smaller).

This has nothing to do with Perl, you would get similar results in C or in probably almost any language, because this has to do with the way computers are storing decimal numbers internally.

Another much simpler example of the same thing:


Code
print  0.4 + 0.2 - 0.6

prints, on my computer, not 0 but: 1.11022302462516e-16

The lesson of the story is that you should never compare for equality a floating point number and an integer.

If you really want to compare in your example, you should change:

Code
if ($m == 5)

to something like this:

Code
my $epsilon = 1e-14; # define the largest deviation you are ready to accept 
if (abs($m-5) < $epsilon) {
print "$i,chenggong";
exit;
}



Your can read more on the subject in many places, including, for example, "What Every Programmer Should Know About Floating-Point Arithmetic or Why donít my numbers add up?" :
http://floating-point-gui.de/


Or:

https://docs.google.com/viewer?a=v&q=cache:xQj4dpk83JkJ:perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf+&hl=en&gl=fr&pid=bl&srcid=ADGEESjj8O_A-TG0QmdQWjLuRvOdHzHkpIJzSh15a0fh9gSJgBS0kXLMN9h5n-sH3mWjdGVbKasYIzo0-NFDpLWbJ5qXUFaBLfA1ibPGOq5_wbGAIVuLaE4YdPnPnOADLJ7PhPQBEWXs&sig=AHIEtbTO8-b-IS2kWBK3Dt04BDl-i2I6ZQ


(This post was edited by Laurent_R on Nov 17, 2012, 5:41 AM)


Laurent_R
Enthusiast / Moderator

Nov 17, 2012, 8:37 AM

Post #3 of 4 (3570 views)
Re: [xiejun] a dault [In reply to] Can't Post

As an additional point, if you use the Math::BigFloat module, you're more likely to get the desired result.

Change the beginning of your program to:


Code
use Math::BigFloat; 

my $s = new Math::BigFloat '0.81';
my ($i,$h,$n,$m)=(0,0,0,0);
# ...


And now, the program prints out:


Code
$ perl  dault.pl 
2.906523
2.998904
3.093161
3.189312
3.287375
3.387368
3.489309
3.593216
3.699107
3.807
3.916913
4.028864
4.142871
4.258952
4.377125
4.497408
4.619819
4.744376
4.871097
5
20,chenggong


Now, $m est exactly equal to 5.


xiejun
New User

Nov 18, 2012, 6:30 AM

Post #4 of 4 (3549 views)
Re: [xiejun] a dault [In reply to] Can't Post

Thank you for your answer.I think I have known it from your answer.

In Reply To

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives