Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re: Filthy Floats

by THuG (Beadle)
on Jul 16, 2001 at 20:31 UTC ( [id://97076]=note: print w/replies, xml ) Need Help??


in reply to Filthy Floats

Well, what I would likely do is find the first two or three consecutive 0s and drop everything after that. But I'm hoping there is a solution that doesn't involve manipulating the data to cover up the error, but something to compensate for this floating point misrepresentation. And I don't know who's fault it is, Access, W2K, PerlScript, or ActivePerl.

This is the kind of problem that someone needs to fix. Guess I can write to ActiveState and let them figure out if it is their problem or MS's.

Replies are listed 'Best First'.
Re (tilly) 2: Filthy Floats
by tilly (Archbishop) on Jul 16, 2001 at 20:39 UTC
    It is the fault of mathematics, and not any particular one the groups you named.

    Floating point numbers simply cannot be stored precisely on a computer. And the most efficient representations to use do not round off to nice-looking numbers in base 10. So what is probably happening is that Access is storing the floating point number as a float. Perl stores it as a double. And since Perl stores it with much more accuracy than it was stored in the database with, when you display it in Perl you can see the round-off error.

    UPDATE
    merlyn is of course right. There are a finite number of exceptions. How many and what they are depends on the particular hardware you have, and the particular physical representation of a floating point number that you choose to use.

      Floating point numbers simply cannot be stored precisely on a computer.
      I know what you mean, but it's not what you said. {grin}

      Insert Some at the beginning of that sentence to make it true. I can certainly represent "0.5" precisely in IEEE floating point. And actually, it might make more sense to say:

      Most fixed-decimal values cannot be represented precisely as binary floating-point numbers, no matter what the precision, because 1/10th is an infinite repeating fraction in binary. Unless the number is an integer divided by a power of two, you'll get some sort of truncation error.

      -- Randal L. Schwartz, Perl hacker

        Most fixed-decimal values cannot be represented precisely as binary floating-point numbers, no matter what the precision, because 1/10th is an infinite repeating fraction in binary. Unless the number is an integer divided by a power of two, you'll get some sort of truncation error.

        Kinda makes me long for the days of BCD arithmetic on an old Motorola 6502c -- there was a chip that knew how to handle base-10 arithmetic! *g*

        Seriously, though, it makes me wonder about constructing a "BCD-based float" object; the significant digits are stored in BCD (binary coded decimal, for those of you too young to remember) format, and the exponent is stored as a signed short -- you'd get 11 digits of precision out of a packed 8-byte structure. The unfortunate part is that you would have to emulate the BCD arithmetic in software, increasing the computation times considerably.

        hehehe just to be picky, one more thing: Tilly said there were a "finite number" of exceptions. If you want, I can demonstrate that the number of exceptions (ie. the number of reals without terminating base-2 representations) is not only infinite, it is also uncountable. *g*

        Spud Zeppelin * spud@spudzeppelin.com

Re: Re: Filthy Floats
by TheoPetersen (Priest) on Jul 16, 2001 at 20:38 UTC
    The problem isn't in ActiveState's product or Microsoft's (but feel free to blame Microsoft anyway; I use them as a general blame sink). The problem is that floating point values do not represent exact decimal numbers. If you want exact decimal numbers, use integers to represent them and then shift the decimal point to the correct position.

    You'll find most database products offer to do this for you. Unfortunately when copying the stored value from the database to Perl, it gets stored as a floating point value again. To preserve accuracy you need to keep the value as an integer until it presentation.

      Okay, I went to University for a BS in Computer Science, so I understand the idea behind floating point representation (and single and double precision, and underflow errors).

      But I am finding it hard to believe that Perl can't represent 0.98. So I did a little test. The DB field is set to string and it is literally ".98". I can pull it from the DB and when I go to display it, I make sure it is displayed as a numerical value and not a string (<%=$target * 1%>).

      If I leave the * 1 out, it shows the string as it is in the DB ".98". If I put the * 1 in, it shows the value "0.98".

      Is Perl doing some magic and not treating ".98" as a float when it needs to computer it with an integer? I figured I would try * 0.5 instead of * 1. Guess what, I get "0.49".

      Is Perl doing that much magic, or can it represent 0.98 as a single or double float?

        Perl does lots of magic, especially in the way it handles scalars as strings, integers and floats simultaneously.

        In your case, the value is being handled as a string until you force Perl to treat it as a number (by using it in a multiplication). The result is a floating point value, subject to the inaccuracies described elsewhere. If it comes up with a seemingly exact value sometimes, well, welcome to the mystery :)

        I take it the issue here is that you want a leading zero, or some other normalization? If so, then use sprintf to enforce the format you want. Try <%=sprintf("%1.2f",$target)%> for instance.

        I am finding it hard to believe that Perl can't represent 0.98

        printf("%.25f\n", 0.98);
        It's not perl, it's the underlying floating points which can not precisely represent 0.98, this is due to floating point numbers being base 2 and having a finite size.
        Perl 6 will have "big rats" that will represent 98/100 exactly. For now, you can use ints and do it yourself. Are you doing arithmetic on the value you fetch? I assume so, or it would not be converted from a string to a number. So, fetch the value, and use a regex to remove the decimal point and note how many digits were to the right of it originally. Now value X becomes (Y*Z) where Y is your integer and Z is the implicit power of ten. Do your math with Y, and also see what becomes of Z in the formulas but keep it implicit. When displaying stick the decimal in the string based on Z, but don't divide.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://97076]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (4)
As of 2024-04-25 05:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found