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

I have a piece of code that looks a bit like this (slightly simplified for pasting purposes):

print STDERR "1:$orderid:$remainingamount > $requestedamount\n"; if ($remainingamount > $requestedamount) { bbcart::report_error(message => "$orderid:Amount approved by serve +r is more than the requested amount. So exiting."); die "Amount approved by server is more than the requested amount." +; }

Sometimes this code seems to trigger and report the error when the amounts are the same. I have received error reports like this:

12345:Amount approved by server is more than the requested amount. So exiting.

When I go to the error, log, it says this:

1:12345:2008 > 2008

But this doesn't always happen, just some of the time.

I am completely at a loss as to how to debug this. Can you think of any possible avenues of investigation? There is nowhere else in the code that outputs this error message. The variables are being printed with identical values immediately before the > comparison claims they are different.

What might possibly cause this?

This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

The code is running under mod_perl in 1.x Apache.

Replies are listed 'Best First'.
Re: Numeric Comparisons Randomly Giving the Wrong Result
by GrandFather (Saint) on Sep 30, 2010 at 11:15 UTC

    Read the replies to When 1000 is less than 1000.

    The short version is that floating point values can not precisely represent decimal fractions like 0.1. The result is that small errors are present whenever you perform arithmetic in computers using floating point representations of decimal fractions. Those errors are often not shown because they are small, but lead to the sort of strange results you have seen.

    If you are dealing with monetary values you can avoid the problem by performing all calculations in, for example, cents then convert to dollars and cents for display. Even then, if you perform mark up, discount, interest, tax or other calculations you will have to take care to round results at an appropriate point and always compare integer cents.

    True laziness is hard work
Re: Numeric Comparisons Randomly Giving the Wrong Result
by salva (Canon) on Sep 30, 2010 at 11:16 UTC
    What might possibly cause this?
    Floating point rounding errors!
    DB<1> p 2008.000000000001 2008 DB<2> p 2008.000000000001 == 2008 DB<3> p 2008.000000000001 - 2008 9.09494701772928e-13
Re: Numeric Comparisons Randomly Giving the Wrong Result
by halfcountplus (Hermit) on Sep 30, 2010 at 12:06 UTC
    Here's an important fact about "floating point" numbers as stored in a computer: they are not true decimal numbers, because computers can only store binary values. That means floats can only accurately represent a number that is an inverse power of two, eg, 1/2, 1/4, 1/8, 1/16 and so on. Notice that does not include 1/10, aka 0.1. You can observe this on your system using perl:

    for (my $i = 0; $i<30; $i += 0.1) { print "$i "; }

    If you run that, you will notice frequently the numbers ARE NOT an even multiple of 0.1, eg, with 32 bit floats you get "5.9 5.99999999999999 6.09999999999999 6.19999999999999" "22.5 22.6000000000001 22.7000000000001 22.8000000000001", etc. In fact, they are NEVER an even multiple of 0.1, but this is not always clear since there is also a limit to the number of "decimal" places when the number is rendered in non-binary form.

    This is not just a perl issue, and there are notorious legends of accounting software disasters because of it; it's why when you are programming applications involving dollars and cents and division, always:

    use integer;

    Then use cents, not dollars, and remember, this is rounded down -- if you want to include fractions of a cent, decide how many decimal places you need and use that fraction as a base unit. Money is not a floating point measure. It is of fixed precision.

      Is using the integer pragma such a good idea? From the documentation:
      1. use integer; 2. $x = 10/3; 3. # $x is now 3, not 3.33333333333333333

      If only addition and subtraction are being used, should be fine - if other arithmetic operations are happening, wouldn't that result in a loss of precision?

        Only if your units are not whole, indivisable units already. If you do all your calculations in cents and do not allow sub-cent amounts, then you won't need to care about rounding.

        If you do fancy calculations, like repeatedly adding fractional parts of things, you need to give deep thought to all your calculations anyway, whether they are in floating point or integer numbers.

Re: Numeric Comparisons Randomly Giving the Wrong Result
by zentara (Cardinal) on Sep 30, 2010 at 11:08 UTC
    I am completely at a loss as to how to debug this

    Well the first thing to try is to capture the equality, so maybe write some code using >= instead of >, and see what happens.


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Numeric Comparisons Randomly Giving the Wrong Result
by mjscott2702 (Pilgrim) on Sep 30, 2010 at 11:54 UTC
    As others have already pointed out, you are running into floating-point errors. There are a number of solutions you might want to try:
    • Use only integer values i.e. use cents/pennies/bah instead of dollars/pounds/foo (already mentioned previously)
    • The use of a module such as Math::BigInt might help here?
    • Could potentially round the values, using sprintf and doing a string comparison - this may suffer from the same issue as you are already seeing.
    • There may be arbitrary precision Math modules already out there? Anyone?

    If this is a financial application, then the use of integer values, if possible, should be the way to go.

      That should be cents/farthings/bah

        Do they still have farthings? They're not worth a ... well, you know.

        As Occam said: Entia non sunt multiplicanda praeter necessitatem.

      Using integer values, you're still stuffed. How do you calculate 17.5% of 100, using only integers?