Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris

Floating point problems

by bluescreen (Friar)
on Oct 23, 2010 at 15:11 UTC ( [id://866960] : perlquestion . print w/replies, xml ) Need Help??

bluescreen has asked for the wisdom of the Perl Monks concerning the following question:

Hi, we recently found some oddity when it comes to how perl threats floating point numbers. For example:

>perl -e'print 37.73*100' 3773


>perl -e'print int(37.73*100)' 3772

Although I can workaround it using sprintf or use bignum. I don't understand why perl is intelligent enough to print 3773 when not using int but it is not smart enough to do same thing when using it. I know internally the number might be 3272.999999999999999 and so int chops off the decimal part but shouldn't be consistent?

This represent a legal problem for our application we rely heavily in maths for prizing and charges calculation.

Replies are listed 'Best First'.
Re: Floating point problems
by Corion (Patriarch) on Oct 23, 2010 at 15:29 UTC
      Do your calculations in cents, or thousandths of cents or whatever precision is needed.

      Don't get me wrong but it seems to be an anti-pattern and really hard to maintain. Not only in monetary calculations suffer same problem, that subtle rounding difference can create a whole mess in scientific applications too.

        Sure, but floating point calculations are not the solution. Either do a calculation of the error introduced by your calculations and the inexactness of floating point calculations, or avoid floating point calculations. Also read the article I linked. If you need a reproducible and exact calculations, use fixed point calculations.

        it seems to be an anti-pattern
        No. It is not an anti-pattern here at all.

        In fact is is the ONLY sensible thing to do.

        That floats are not suitable to reprensent monetary amounts (ultimately because 0.1 is not a finite decimal in binary) is in fact so well known and has been so many times advised against that I am in fact surprised that anybody would consider this even for a second.

        With respect, but if your floating point code creates "a whole mess" in scientific applications you are not writing good quality code.

        Numerical analysts have recognized the issues which arise from rounding errors, truncation errors and quantization errors for a very long time now, certainly since before the invention of electronics. For around 200 years, since the days of Gauss at least, techniques and algorithms have been developed to help minimize inaccuracy in chains of computations on finite representations of real numbers.

        Anyone who wishes to find out more on the subject should consult texts on numerical analysis. There are many good ones available.


Re: Floating point problems
by shevek (Beadle) on Oct 23, 2010 at 16:14 UTC
    I agree that it does seem odd that perl apparently uses a different rounding methodology for the int() function versus doing the calculation in print. But, there is no cut and dry way to handle this. sprintf and printf use round half to even. If you want a explanation of all of the different rounding strategies then search wikipedia for round half to even.
    foreach my $i (0.5, 1.5, 1.7, 2.3 ,2.5, 2.7){ printf "%2.0f", $i; }
    Output: 0 2 2 2 2 3 As you can see, it does round toward even. Here is another example with int() that doesn't work the way you think it would:
    print int(0.6/0.2);
    You would think the above would print 3, but it prints 2. The following works like you expect and prints 3.
    print 0.6/0.2;
    The short is that there are several methods for rounding, and you really need to know when and where each method is used or you don't use floating point maths to calculate money until displayed for output. Also, you should read "What Every Computer Scientist Should Know About Floating-Point Arithmetic". You can find it by googling. People have different uses for different rounding schemes, again read about rounding on wikipedia. These different needs means that not all functions will round the same. By the way, this is not just a perl problem/difference.
      For what it's worth, ActivePerl prints 1 2 2 2 3 3. In other words, it uses "round half away from zero" (languages, and even different compilers and interpreters for the same language, are inconsistent in this regard -- see my article
Re: Floating point problems
by PeterPeiGuo (Hermit) on Oct 24, 2010 at 01:08 UTC

    perldoc is very clear on this: int() simply returns the integer portion, so there is no rounding in the conventional sense.

    Peter (Guo) Pei

      Yeah int is very clear about that, but print isn't.

      Am I asking too much if I see something prints 3 the int of that to be 3 ?

        Am I asking too much if I see something prints 3 the int of that to be 3 ?

        In a word: yes.

        int has a very clearly defined meaning: truncate to an integer. And that behaviour is required for many algorithms.

        It never "rounds-up" as print does:

        printf( "%.16f ", 3-$_ ), print 3-$_ for map 10**-$_, 1 .. 16;; 2.8999999999999999 2.9 2.9900000000000002 2.99 2.9990000000000001 2.999 2.9998999999999998 2.9999 2.9999899999999999 2.99999 2.9999989999999999 2.999999 2.9999999000000002 2.9999999 2.9999999900000001 2.99999999 2.9999999989999999 2.999999999 2.9999999999000000 2.9999999999 2.9999999999900000 2.99999999999 2.9999999999989999 2.999999999999 2.9999999999999001 2.9999999999999 2.9999999999999898 2.99999999999999 2.9999999999999991 3 3.0000000000000000 3

        If you want rounding behaviour rather than truncation, use an appropriate function or module.

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Floating point problems
by toma (Vicar) on Oct 24, 2010 at 07:24 UTC
    Instead of int(), I prefer to use one of the functions in Math::Round.
    It should work perfectly the first time! - toma
Re: Floating point problems
by bluescreen (Friar) on Oct 24, 2010 at 15:54 UTC

    I really appreciate everybody's opinion here, really do. I've try other languages and they all have the same behaviour:

    (0.6 / 0.2) asInteger 2
    python -c 'print int(0.6/0.2)' 2
    ruby -e 'print (0.6/0.2).to_i' 2

    In the end I believe Dynamic languages ( perl, ruby, python ) should handle cases like this in a more elegant and seamless way.

    Dynamic languages do a pretty job hiding the internal representation of numbers for the user, in the sense that you can create program without knowing whether a variable holds an integer, double, float, fixed precision or any other representation. We leave the details of picking the right representation to the compiler/interpreter, so we (users) could expect to handle such border cases also seamlessly.

    As a final though,

    • do you think that everybody would easily spot why int(0.6/0.2) returns 2 instead of 3 ?
    • If print 3.0 returned 2.99999999 would you tell users that interpreter is doing the right job and users should stay away of using number with decimal places?
      But int is defined as truncating towards zero, if you do not want that behaviour use sprintf.

      If you could make int magical and sometimes behave differently. How would you avoid false positives?