The Ultimate Business Machine - Archives List of Categories : Database * Technology * Commentary * Singapore * Travel *
Fri 08 Sep 2006
Business, Computing, and the Mac
Category : Technology/numberTest.txt
I realised I hadn't written anything in three weeks. But I'm only now re-surfacing, having spent all that time inside the bowels of Luca, pulling out all references to doubles and floating point numbers, and replacing them with Cocoa's NSDecimalNumber class. Is it worth it? Consider the following code snippet : - (void)test:(id)sender {     NSDecimalNumber *decNo = [NSDecimalNumber                 decimalNumberWithString:@"0.0001"];     NSDecimalNumber *total = [NSDecimalNumber zero];
    int i;     for ( i = 0 ; i < 10000 ; i++ ) {         total = [total decimalNumberByAdding: decNo];     }     NSLog(@"total, as NSDecimalNumber = %@", total);
    double numberAsDouble = 0.0001;     double totalAsDouble = 0.0;
    for ( i = 0 ; i < 10000 ; i++ ) {         totalAsDouble = totalAsDouble + numberAsDouble;     }     NSLog(@"total, as double = %1.24f", totalAsDouble); }
So, we've got two numbers. One expressing 0.0001 as an NSDecimalNumber and the other as a floating point number. We loop around and add them up 10000 times. These are the results we get : [Luca 1397] total, as NSDecimalNumber = 1 [Luca 1397] total, as double = 0.999999999999906186154419 With the NSDecimalNumber, we get the result we expected, 1. The floating point number, on the other hand, came very close to 1 but then, not quite. That's the problem we face working with floating point numbers. They really do float away from what they are meant to be. Let's see what havoc this causes in an accounting application. Let's say we're adding a long list of debits and credits. The two columns often won't balance, if we don't apply a mathematical function to round the figures. But then, when do you do the rounding? After the addition, or on each line item? And to how many decimal places? Two, if you're working with US dollars, none if we're working with the Japanese yen. The code easily becomes very complicated - unecessarily so because if the computer knew how to do its math, we wouldn't have to round at all. So that's what NSDecimalNumber does - it lets you work with greater precision so you get the result you want without having to go through all these contortions. That doesn't mean you eliminate rounding completely. You still have to apply a rounding function when you multiply, say, a monetary figure by an exchange rate, and you have to decide how many places you want to round it to (probably to the number of decimal places used by the currency you base your accounting system on). And you still have to work out how to handle dividing 1 dollar between 3 people, like who gets to keep the last cent. But at least we've cleared up, say 80% of the clutter in our code. So, imprecise calculation is one problem we faced in business computing that we can solve with that new data structure, called the NSDecimalNumber in Objective-C, and BigDecimal in Java. Then there's the problem of storage. Does the database system handle exact-number mathematics? Luckily, MySQL does, with its Decimal data type (since MySQL 5.0.3). So shuttling the data between Luca and MySQL is sweet - things go in and out the way you want. Not so with SQLite3. You get all this muck (in red, below) because SQLite still works only with floats and doubles and it can't represent a number like 513.58 exactly : The problem comes when you read the data back. (Again, just when do you round the data, and to how many decimal places?) I'll have to attack this problem next. But let's move on. I keep mentioning that we need to figure out how many decimal places to round, say, an exchange rate calculation to, because it depends on the context. In the picture below, I show a Luca payment voucher where three currencies are involved. The original invoice was billed in Singapore dollars, and is being paid with Malaysian dollars. The base currency (the local currency used by our fictitious example company) is in US dollars and that's what we use to book the bank charges and the currency exchange gain under. In this case, all three currencies are denoted to two decimal places. But if we get paid in Yen, we'd like, as Mac users, to see no decimal places at all when we're entering the Yen value. And when we print out that voucher we'd like to see the Yen symbol being printed, too. Fortunately, Cocoa has a user-interface class called the NSNumberFormatter class, that I have built a custom sub-class for, and that I can use inside Interface Builder, as if it were one of the supplied palette objects : And the great thing about being able to use your own custom number formatter class is that it opens up a lot of possibilities. Suddenly, you're able to format numbers the way the French or the Swedes or the Indians want, and since you can get hold of your formatter object in your code during run-time, you can do a lot of currency formatting on the fly, or let the users customise it themselves. If the user decides to format the Yen to no decimal places, you will be able to tell from the "Max fraction digits" parameter, above, and that's what you use to control the rounding behaviour. Plus you can set the rounding behaviour to say, the traditional way (which has a bias towards rounding up), or "bankers' rounding" where numbers are rounded down as often, statistically, as they are rounded up. And you can gather all these customisations in one logical place, so it's possible to avoid hard-coding anything in your application, and thereby making it all truly multi-currency. Cocoa is a wonderful development environment. Sometimes, like when I managed to get this working in Interface Builder, I was so excited and I wonder why don't more people see it? This is so great. Like, with the Spell Out Style, above, you can do cheque printing. And, though I haven't done it yet, you should be able to print it with fillers like asterisks between the currency symbol and the money value. Now, if all these are not doing things to support a business, then I wonder what is. And if the Mac is not a business machine, then what is? It took me three hours, from knowing nothing about Interface Builder palettes, to actually creating my own number formatter and getting it working in my own nib files. And I actually did this while parked near some people noisily playing mahjong in some relatives' house. I say this because I'm constantly amazed at how productive this development environment is. And yet how little people know about it in the mainstream.
Posted at 7:05AM UTC | permalink
|