RSS Feed

Coding Money in Gnucash for Android

Posted on Wednesday, July 18, 2012 in Coding, User

So here I am implementing happily along in Gnucash for Android. For the most part, everything works out fine and all the automated tests implemented so far pass on the target devices. Great! But sometimes, and only sometimes, I notice that when I open a transaction to edit, it is not exactly the amount which I put in there which is displayed. Sometimes, the last digit has been rounded off, or dropped completely. This is strange, and annoying because it is not reliably reproducible. So I keep trying to reproduce it and finally notice that it mostly occurs especially when I enter big amount, like with 7 digits and up. This is good, because now I can somehow reproduce it. The question is, what is happening?

Now, before I continue, I must say that after the alpha release, similar reports of crashes came in all related to recording transactions. Not all of these were due to what I am about to describe. Mostly, the crashes were due to bugs with the parsing of the money and currency.

First of all, it is money we are dealing with. You do not want to mess with your users’ accounting figures and of course people will run from any software which rounds their figures. So I set about investigating what was happening and how I could remedy it. When I enabled the debugger, I immediately noticed that the internal representation of the figures was not always straight forward. Once the number becomes big enough, it is stored (or at least shown in the debugger) in the scientific format or rounded off in some way which loses precision. So if someone entered a figure as 18975699, it was held in the double as1.8976×107 . Who wants to see their money in that format? Not me!


The problem is when coding money, you always have the internal representation of the value, and that which the user sees. So you have to convert your amount into a string to display it, while saving it in a way which keeps its precision. So I started searching about how to best represent money in Java. It turns out that floats or doubles are a big nono for money amounts. They cannot accurately represent base 10 numbers. I thought I was fine since I was using doubles instead of floats. But they are not any safer. BigDecimals were mostly recommended for anything which needs laser precision (like money).

Most of the users of the alpha version of GnucashMobile for Android have not noticed this particular bug yet because in a mobile expense tracker application, mostly smaller amounts are entered and doubles are usually large enough to hold the values and display them. But you can never predict what you users will input and it is better to anticipate anything.

So after refactoring the code to use BigDecimal, I reran my automated tests without updating them and hoped they would fail (sweet irony). Sure enough, they did. When validating the amount fields, I got the log message “AssertionFailedError: expected <9.9900000000000000213162820…..> but was: <9.99>.” $9.99 was the actual value entered but the tests still run with double values, which tags along some extra digits for the ride. I will be updating the tests shortly.

You might argue that they are in effect the same after rounding, but when it comes to money, I like the thought that when my users enter $9.99, it is actually $9.99 that gets saved and nothing else. And I think my users would like that too. It’s money we’re talking about after all Winking smile.

Share and Enjoy:
  • Twitter
  • Google Bookmarks
  • Digg
  • Facebook

Bring on the comments

  1. Steven says:

    I hope I don’t come off as rude or rubbing it in but…

    You NEVER EVER EVER use a float or double when dealing with money in ANY language! NEVER!

    Now a bit more advice (that might cause you to refactor again). While BigDecimal is pretty good for money calculations it does have it’s problems. Division that produces a recurring decimal will throw an ArithmeticException unless you supply a rounding behavior and scale (scale is the number of digits to the right of the decimal).

    There are generally two approaches to dealing with money either track using a class like BigDecimal (if available in the language you are working with) or using long values stored as a number of fractions of a cent. In either case you need to know ahead of time how many decimal values you are tracking. Since this is a Gnucash android app I would recommend finding out what precision Gnucash uses. There are also specific rounding rules in certain currencies (such as currencies where the smallest value is .05 rather than .01).

    There is a specific (and rather simple) rounding algorithm that basically involves multiplying the number, truncating, and then diving again, but I don’t remember it off the top of my head. Google might help.

    • Ngewi Fet says:

      Thanks for the input. I have learnt that lesson now.
      I decided to go all the way and create a Money wrapper class (which also stores currency) and I also always specify what rounding and scale to use in the arithmetic operations. RoundingMode.HALF_EVEN seemed to be the universally recommended rounding for money, so I went with that. GnucashMobile uses a scale of 2 and that should be sufficient for now.

  2. Rudd-O says:

    Default GnuCash has the same behavior. Very irritating — makes it useless to account for Bitcoin.

  3. kiers says:

    Wow, i never expected computers can be SO convoluted in handling basic numeric calculation! I was using jabp lite in android (also written in java) and found data corruption and crashes in the android version and have now started using gnu. I thought even most basic calculators with cheap processors built in had MASTERED simple calculation, but no.

Leave a Reply