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.
February 10th, 2008 at 12:50 pm
round() ?
February 10th, 2008 at 1:00 pm
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.
February 10th, 2008 at 1:18 pm
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.
February 10th, 2008 at 1:30 pm
Use COBOL. Remember BCD?
PIC 99,999,999.99 USAGE IS COMP-3.
February 10th, 2008 at 1:55 pm
What’s more, the table of items is horribly out of alignment with its header. I second the recommendation to use Cobol.
February 10th, 2008 at 2:13 pm
How about using java BigDecimal to represent all the currency.
February 10th, 2008 at 2:21 pm
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.
February 10th, 2008 at 3:43 pm
BigDecimal? Wouldn’t using long to store the number of pennies be a lot easier?
February 10th, 2008 at 4:03 pm
I think long would be fine as long as you’re not doing financial calculations that involve percentages. Those fractional pennies add up.
February 10th, 2008 at 4:03 pm
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.
February 10th, 2008 at 4:04 pm
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.
February 10th, 2008 at 5:34 pm
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.
February 10th, 2008 at 5:41 pm
Except they weren’t charged 9.879~, they would have been charged 9.88.
February 10th, 2008 at 6:01 pm
Adrian,
(9.879~) == (9.88)
Not approximately – exactly.
If you want to nail me for something, nail me for misspelling Dijkstra’s name.
February 10th, 2008 at 6:02 pm
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.
February 10th, 2008 at 6:20 pm
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.
February 10th, 2008 at 10:36 pm
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
February 10th, 2008 at 10:45 pm
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
February 10th, 2008 at 10:56 pm
And the shipping URL page should have the order number string pre-appended (like “shipping.jsp?order=#####”). Another less semantic load ๐
February 11th, 2008 at 6:32 pm
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.
February 13th, 2008 at 10:38 pm
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.
March 5th, 2008 at 2:38 am
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.
March 9th, 2008 at 10:58 pm
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.
April 22nd, 2008 at 4:42 pm
Take a look at this post: http://jug.org.ua/wiki/display/JavaAlmanac/The+Need+for+BigDecimal+-+Core+Java+Technology+Tech+Tips
January 18th, 2009 at 12:04 pm
[…] 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 […]