7 Top Tips for Coding With Currency

As anyone who’s ever made an e-commerce system knows, money is everything. No, really if you don’t get your financial sums right then you can’t hope to build a successful online business (and may find yourself creating potential legal issues with the tax man).

So here’s a rundown of the top tips I can give for making your financial calculations that bit easier.

1. Always Work in Minor Units

I can’t stress enough how much this helps in terms of accuracy, rounding and speed. Working in major units may look better to you as you don’t have to reformat the numbers to display them but I hope I can make the case here for minor units.

a. Integer arithmetic is much much faster than floating point arithmetic.

Remember that even a single decimal place makes a number a float as far as your computer is concerned and all the processor overheads that go along with them suddenly arrive. I know it’s not a lot slower but in a complex financial system it all adds up believe me.

b. Floating point arithmetic can get its sums wrong.

Don’t believe me? Then pull up a Ruby console and try this:

a = "69.54".to_f
b = a * 100
b.ceil

Gives 6955 instead of 6954. This is because the limitations of floating point arithmetic have caused something like 0.0000000000000000000000000000000000000000000000001 to be added to 69.54. I spent a good 4 hours chasing this bug which manifested itself as a 1p discrepency.

c. Trailing zeros can cause problems for major units.

Think of trying to pass round £10.00 or £1.10 in major units. Storing it as a float you would keep losing the trailing zeros and would find yourself having to sprintf all over the place. I’ve seen plenty of systems in my time that store prices as decimal strings to get round these issues!

There are of course various decimal formats that can be used (decimal is a data type in MySQL and BigDecimal has been introduced in Ruby on Rails) but when it comes down to it, these are just wrappers around either floats or stings and majorly sub-optimal for the other reasons given.

2. Freeze the Exchange Rate

If your business works in pounds but you allow payments to be made in Euros then with every payment you need to store the current exchange rate with it. Exchange rates change by the day and if you don’t know exactly what rate you get for your transactions then you can kiss goodbye to any sort of accurate profit calculations.

3. Rounding: Pick a Direction and Stick With It

Often you will need to apply discounts, add markup etc and have to perform percentage calculations. If you are working in minor units this should be the only time (in normal day to day operations) that you ever have to handle fractions of pence. You will make your life so much simpler if, for all these calculations you decide the direction to round and stick with it. Do you want to keep the extra for yourself or be a nice guy and let the customer keep it? That’s what the decision comes down to.

If you don’t have consistency in this you really will find yourself spending days chasing 1p discrepancies.

From a coding point of view I tend to round down as preference because (as I demonstrate above) floating point arithmetic can get it wrong sometimes and, as it just wipes out everything after the decimal point, a ‘floor’ function is much more reliable that a ‘ceil’.

4. Use a Pre-Filter On Your Submissions

Of course your customers are always going to want to work in major units – no-one wants to see prices in pence splashed all over your website and it’s much more intuitive to type major units into form fields.

What I like to do is put a pre-filter on all input coming into my back end system (so in Rails you would run the filter on ‘params’ or in PHP you would run it on ‘$_REQUEST’) which pattern matches any string monetary amount (remember, all form submission values come through as strings) in a major unit and converts it to an integer minor unit.

In Rails it’s in the application controller and looks like this:

def filter_units (input)
 if [Array, Hash, HashWithIndifferentAccess].include?(input.class)
  input.each do |key, value|
   #recurse through the data structure
   input[key] = self.filter_units(value)
  end
 #match the string format for a major unit
 elsif not input.nil? and input.match(/^\d+\.\d\d)$/)
  #convert to a minor unit integer
  (input.to_f * 100.0).to_i
 else
  #return the value unchanged
  input
 end
end

This also has the added benefit of validating monetary amounts – if a monetary field doesn’t hit your back end as an integer then you know it has failed validation.

So there you have it. I’m not saying these are all the answers to a coding trouble-free finance system for your e-commerce store, but they’ll give you a solid starting point and hopefully avoid a few hours of head-scratching.

21 comments ↓

#1 anon on 10.07.08 at 5:24 pm

Nice article. I agree to a point that you should use minor units when possible, but there have to be exceptions (you had to have seen this coming). I worked at a big bank that’s buying up other banks right now (rhymes with JP Schmorgan). Sometimes we’d deal with fractional amounts to three or four decimals and never do any calculations. Would the base unit of calculation then be 1000th of a cent? Not really an options. One could argue that using Strings in this situation would be better, and I’d have to agree. However floats weren’t so bad and nothing ever got muddled (I think).

#2 Raoul Duke on 10.07.08 at 6:37 pm

i would have thought surely by now there is some standard library for dealing accurately with money? some sort of arbitrary-precision thing?

#3 Pavel on 10.07.08 at 7:37 pm

You worked for a fancy bank and you don’t *think* anything ever got muddled?

brb withdrawing (hopefully) all my money

#4 Me on 10.07.08 at 8:28 pm

This is simplistic. Use something with support for IBM Decimal specification, at http://speleotrove.com/decimal/ .

You suggestion “Rounding: Pick a Direction and Stick With It” can be illegal in some places.

Note as an example http://europa.eu/scadplus/leg/en/lvb/l25025.htm which has that conversion rates are to 6 significant digits (not 6 decimal places) and which defines explicit rounding rules that one must follow.

#5 Steve on 10.07.08 at 9:52 pm

Not that I disagree that you should not use floats, but your 1p example is stupid. If such a calculation causes 1p discrepancies why would you only store to the accuracy of 1p? Why not add 2 more digits. You can do the arithmetic *1000 instead of *10 and it still works out. Best of both worlds.

#6 David Grant on 10.07.08 at 10:29 pm

Just curious, why would you be doing a ceil in the first place?

#7 ruertar on 10.07.08 at 10:48 pm

Then your base unit should have been 1/1000 of a cent.

Float would be bad and strings would be an awful way to store it.

If you used 64bit ints to store your 1/1000c data, you could still store about 18 PENTILLION dollars (18*10^15).

#8 Jeremy on 10.07.08 at 11:25 pm

I built a Rails app using decimal storage (extending ActiveRecord to always return BigDecimals) and later found a couple places where I performed calculations against Floats which caused incorrect addition and rounding. (Also converting to YAML caused the values to be lost.) If I needed fractional cents like anon I would use that strategy again.

In Ruby, the Money class (http://dist.leetsoft.com/api/money/) is somewhat decent, but would need some tweaking to automatically pull and freeze current exchange rates (I’ve only used it for USD).

#9 Nathan on 10.07.08 at 11:52 pm

I don’t know what’s more scary: suggestions of rounding away amounts or banks using ruby. :)

Always always use BigDecimal (or equivalent). Steer clear of floats and doubles like the plague. If you use these then it doesn’t matter whether you use minor or major currency amounts.

My experience in banking was that we needed to keep track of fractions of cents. That was trading though, perhaps other applications are more lax.

Also worth noting: some currencies have such huge numbers they’re stored in units of 1000 or higher. And not all currencies have dollars/cents, pounds/pence style. E.g. most of the asian currencies.

#10 Aaron Davies on 10.08.08 at 2:23 am

My favorite thing is when the “minor unit” keeps changing. I’ve seen stock trades go by at sub-basis-point pricing recently. (A bp is 1/100 of a cent (1/10000 of a dollar) for those of you not in finance, so we’re talking about dollar pricing accurate to five places.) What am I supposed to do, rewrite all the bp-based accounting in ppm?

#11 Robert Lee on 10.08.08 at 6:15 am

Anon: Please fire yourself as soon as possible. You are the last thing the banking sector needs right now.

To anyone else reading that:

The correct solution is to ALWAYS store money in an integer EXCEPT when your platform provides a reliable format specifically designed to handle currency (e.g. IBM’s Decimal Spec implementation).

If you need thousandths of a cent precision, then your integers do indeed contain thousandths of cents. In fact, odds are you will need sub-penny accuracy in most complex monetary systems (even if just for error correction).

Since you will be using a lot of digits for precision, make sure your platforms have native support for 64 bit signed integers, and make sure you are using them.

As a rule, I always use 8 digits of precision after the PENNY (10 after the dollar). This makes the databases forwards compatible for example. In the application logic I do absolutely no rounding except on display. This prevents most rounding errors and keeps the system really simple. Also, I reuse the same class for handling all rounding (if I ever need to change the rounding policy, I only have to update that one class).

Just my $0.02.

#12 John Main on 10.08.08 at 9:22 am

Thanks for commenting guys

@anon – As I’ve said I think the only way to go is integer arithmetic and I do think that if you work in thousandths or ten-thousandths of a penny/cent/whatever then that’s the base unit you should use. As ruertar says, with 64bit ints that still allows you to represent pentillions of your major unit.
And I also agree with Pavel – that’s rather worrying…

@Raoul – If you find one that always gets its sums right and doesn’t clog the CPU with excessive floating point arithmetic and/or casting to and from strings then please send me a link :)

@Me – Of course you have to follow your spec and not make any arbitrary decisions about rounding. I work a lot with percentage discounts, markup etc and believe me, always rounding in one direction makes my life so much easier!

@Steve – To me that is treating the symptoms rather than the disease. If the rest of the system works perfectly well in minor units then increasing your accuracy to handle a single bug is unnecessary complication, especially when there are easier ways round it.
In this example we would be increasing the accuracy but then always wanting the additional least significant digits to be zero as any other value would represent the floating point error.

@David – I work a lot with percentage discounts and, being the nice guys we are, we round them up to the nearest penny. Hence the ‘ceil’.

@Jeremy – Nice one, I’m off to check out that money class right now

#13 Currency on 10.08.08 at 7:27 pm

[…] Main has 7 top tips for coding with currency – with Rails specifically in […]

#14 Matt on 10.09.08 at 1:52 am

Use a decimal and be done with it.

#15 links for 2008-10-08 « Donghai Ma on 10.09.08 at 4:51 am

[…] 7 Top Tips for Coding With Currency | The Matchbox (tags: tips programming business money ecommerce) […]

#16 links for 2008-10-08 | Libin Pan on 10.09.08 at 6:49 am

[…] 7 Top Tips for Coding With Currency | The Matchbox (tags: rubyonrails ruby rails validation money) […]

#17 Dev Blog AF83 » Blog Archive » Veille technologique : Merb, Vim, geode, iPhone, etc. on 10.13.08 at 6:41 pm

[…] : un plugin jQuery pour choisir l’heure en 3 clicks. * http://www.setfiremedia.com/blog/7-top-tips-for-coding-with-currency : 7 astuces pour développeurs confrontés à différentes monnaies. * […]

#18 sambeau on 10.13.08 at 9:54 pm

Just one comment: Floats are not slower than integers any more.

#19 gauda.de » Blog Archive » Use to_money to convert a string to an integer on 11.03.08 at 1:21 am

[…] You can read about the motivation why to use integer in flavor of decimals in the database here: 7 Top Tips for Coding With Currency. […]

#20 Paul Mckay on 11.06.09 at 1:10 pm

Hey John,

Remembered seeing this article a while back and had some recent experience with dealing with currency.

Just a quick tip for everyone, I agree totally that using floats is terrible for currency, and working with minor units as John suggest is a much better way.

Lest this bites anyone in the ass part way through a design, for e-commerce projects, don’t use cents/pennies as your minor unit. The problem with that is VAT/tax calculations. You often want to store the price ex vat against a product and then calculate the VAT afterwards. The problem with storing your ex vat price in cents/pennies is that it’s impossible (due to rounding) to get certain inc vat prices.

For this reason (according to our accountant) accounting packages store prices ex vat to 100ths of a cent/penny. So you are much better off making that your minor unit.

(Bear in mind, companies that sell a lot internationally will charge a different VAT/tax rate according to the country they are shipping to, in these situations storing price inc vat doesn’t work out).

#21 Gawin on 11.07.09 at 1:39 pm

The Money gem now lives at:
http://money.rubyforge.org/

Code on github:
http://github.com/FooBarWidget/money/

gem install money

Leave a Comment