Spot the Bug

A future exam question: Identify the elementary programming error in the following actual output from a real web store.

Bonus credit: describe both the quick emergency fix for the problem, and the longterm fix for the problem.

Greetings from CellularFactory.com.

We thought you'd like to know that we shipped your items, and that this completes 
your order. Your order number is ###### Please keep this number for any future 
requests

You can track the status of this order, and all your orders, online by visiting our 
page at http://www.CellularFactory.com/help/shipping.jsp

The following items have been shipped to you by CellularFactory.com:
---------------------------------------------------------------------
Qty      Item                   Price       Shipped      Subtotal
---------------------------------------------------------------------
1      Travel Charger                   5.89      2008-02-09      5.89
---------------------------------------------------------------------

Shipped via USPS (estimated arrival date: about 4-6 days after)
---------------------------------------------------------------------
Item Subtotal
5.89
Shipping & Handling:
3.99
Total:
9.879999999999999
--------------------------------------------------------------------

This shipment was sent to:

I teach this bug to my Intro to Java students, but I was a little shocked to see it in production. This is why I get nervous ordering from tiddlywink sites like Cellular Factory. If they can’t get the easy things right, what’s the chance they’re implementing proper information security procedures to protect my financial and personal information? Approximately zero, I’d venture.

25 Responses to “Spot the Bug”

  1. gatzke Says:

    round() ?

  2. Peter C O Johansson Says:

    Okay, I may be utterly wrong. I hope not. Fundamental error: Using floats to represent currency, manifested as inaccuracy and being printed with a hueg number of decimals at the end. Quick fix: %.2f. Long-term fix: Start using an integral type instead.

  3. jbs Says:

    Don’t use round().

    The real lesson here is that approximations of numbers (e.g., floating point representations) are inappropriate for exact and enumerable quantities.

    Approximations are fine if you are talking about, say, elastic-strain tension but can be disastrous when talking about, say, monies. The conventional wisdom is to use a fixed point arithmetic library when the exact count of something matters.

    Some languages like Haskell and C# have arbitrary fixed-point arithmetic built-in; others like C require a third-party library (e.g., GMP) or you to roll your own.

  4. Garrett N Says:

    Use COBOL. Remember BCD?

    PIC 99,999,999.99 USAGE IS COMP-3.

  5. John Cowan Says:

    What’s more, the table of items is horribly out of alignment with its header. I second the recommendation to use Cobol.

  6. kalpesh patel Says:

    How about using java BigDecimal to represent all the currency.

  7. Alan Little Says:

    Garrett: you want to hard-code your separators, thus offending/confusing all your customers in Europe where dots are used to mark thousands and the decimal separator is a comma? Or in India where lakhs and crores are delimited every four figures instead of every three? Why? (Or maybe I underestimate COBOL and it has locale settings to handle such things?)

    The blatantly misaligned formatting struck me as just as obvious and amateurish too. I’d be willing to bet considerable sums of money that they can’t handle non-US addresses properly either, and parsing spaces out of credit card numbers is beyond them: these being almost universal characteristics of incompetent ecommerce sites.

  8. josh Says:

    BigDecimal? Wouldn’t using long to store the number of pennies be a lot easier?

  9. brett Says:

    I think long would be fine as long as you’re not doing financial calculations that involve percentages. Those fractional pennies add up.

  10. Martijn Says:

    Sometimes you need to store (or calculate with) smaller amounts than even pennies (yes, really.. take weird amounts of tax for example), so it’s best to go for a few centipennies or even millipennies, and round properly/move the comma on output(!) only.

  11. ronjohn Says:

    this might not be a programming error per say, but it would certainly be advisable to count the basket contents and ship your ‘item’ instead of ‘items’ if there is only one thing in it.

  12. John Vance Says:

    Simple. The program should not have printed repeated 9s. It should have printed a single 9 followed by a tilde indicating infinite repeat – like so:

    9.879~

    The long term solution would be to invest in math education, so that the average user could understand such notation. See Djikstra’s comments about end users.

  13. Adrian Says:

    Except they weren’t charged 9.879~, they would have been charged 9.88.

  14. John Vance Says:

    Adrian,

    (9.879~) == (9.88)

    Not approximately – exactly.

    If you want to nail me for something, nail me for misspelling Dijkstra’s name.

  15. Zack Says:

    Yes. They were charged 9.88.

    Either, the problem is that they used a broken language (java?) with faulty float addition:

    MzScheme:
    (+ 3.89 5.99)
    9.88

    Python:
    >> 3.89 + 5.99
    9.8800000000008

    Or, which seems more likely, they stored the prices in integral cents, converted to floating point, divided by 100.0 to format the output:

    (389 + 599) / 100.0

    which will give you the wacky number they posted in many languages. They should have formatted correctly, yes, but lets hope they also know what is going on with their calculations, as well.

  16. Adrian Says:

    John,

    >(9.879~) == (9.88)
    >Not approximately – exactly.

    Ok fair point. I’d already forgotten http://en.wikipedia.org/wiki/0.999… after reading it a few months ago.

    I’ll have to fall back on arguing that 9.88 is a better representation for the users in this case.

  17. Garren Says:

    Yeah, Java. Just in case the url “…shipping.jsp” didn’t give it away.
    It’s not “faulty float addition” nor is it a “broken language” (debatable). It’s just good ol’sloppy coding.
    A more interesting game might be, “Fix the bug”.

    class phew {
    public static void main( String[] args ) {
    double price = 5.89, tax = 3.99;
    System.out.println( price + ” + ” + tax + ” = ” +
    (price + tax) );
    // ( java.lang.Math.round( 100 * (price + tax) ) / 100.00 )
    }
    }

    5.89 + 3.99 = 9.879999999999999

  18. Ramkumar.E.V. Says:

    Hi,

    I see this from an user perspective.If you see realistic currencies,you can maximum pay to someone for 2 decimal points,you can pay for figures like 12.3456 and all .. you can max pay for 12.34

    So the best deal would be to use double for calculations since it takes care of decimal issues by default or use explicit type conversion to float or use BigDecimal whatever but keep it rounded to 2 decimal points at the end when the results are shown to the user.

    Cheers
    Ram

  19. aadis Says:

    And the shipping URL page should have the order number string pre-appended (like “shipping.jsp?order=#####”). Another less semantic load ๐Ÿ™‚

  20. duncan Says:

    The upstream commenters who point out that some code will have to deal with amounts less than one cent are of course correct, but if you know you will only be dealing with US currency and that you will not have to deal with fractions of a penny, handling currency in pennies makes a lot of sense as a defensive measure. It’s not always appropriate, but where it is I don’t see a problem with it.

  21. Pat Farrell Says:

    There may be more, but the criminal sin is using floating point for money.

    Any language used by folks who might use money to do anything, needs a Money or Currency intrinsic type so that rookie programmers don’t shoot themselves in the feet using floating point.

    I’m not asking for Euro to Pounds Sterling to Dollars, just a Money datatype.

  22. MHMD Says:

    Hi,

    My perspective about analyzing the above code in java would be,

    BUG:
    Total:9.879999999999999
    Expected:
    Total:9.88
    Problem description:
    In Java floating points are expressed as fractions in binary formats , so using double as data type for these would have caused the problem.

    Solution:
    Quick Fix:

    1.Use float instead of double that would give the exact answer need for this case.
    2.In case if the double is needed in anyways then use NumberFormat class to format the output

    Original Solution:
    1.Use BigDecimal and NumberFormat class to format the output.

  23. John Cowan Says:

    Alan Little: Cobol does indeed allow you to say DECIMAL POINT IS COMMA, and you can insert punctuation editing characters wherever you like — you aren’t restricted to putting one every three places. In any case, Indian numbers aren’t every four places: it’s one thousand = 1,000; one lakh (10^5) = 1,00,000, and one crore (10^7) = 1,00,00,000. It’s Archimede’s Sand Reckoner that worked in multiples of one myriad (10^4).

    Zack: If MzScheme indeed prints that, it is violating the Scheme standard, which requires that all inexact numbers be printed using the nearest exact numeral. Python is doing the right thing.

  24. David Madden Says:

    Take a look at this post: http://jug.org.ua/wiki/display/JavaAlmanac/The+Need+for+BigDecimal+-+Core+Java+Technology+Tech+Tips

  25. Monetary calculations in CL | keyongtech Says:

    […] calculations in CL This was posted on reddit — http://cafe.elharo.com/programming/spot-the-bug/ Printed representations aside (that can be taken care of with ~$), how do you perform monetary […]