You better work in cents, not dollars đź’¸

How to avoid common pitfalls when your code handles money

Alex Loukissas
AgentRisk: Superhuman Wealth Management

--

When building an application, some lines of code are more important than others. Screw up your mutex logic and you may run into a deadlock or a race condition. Screw up a database query and you can wipe out an entire table. Screw up how you handle money, and someone will get short-changed (or even worse: you get fined by the SEC).

But how can you screw up how you handle money, you ask?

Hint: you may have snoozed during that boring lecture where the professor was covering floating point arithmetic and scientific notation. Let’s have a quick refresher from Tom in the video below:

Worth watching even if you know everything about floating point numbers.

Why using float to store money is a terrible idea

I couldn’t have said it better than Bill Karwin:

If I had a dime for every time I’ve seen someone use FLOAT to store currency, I’d have $999.997634.

But let’s try this out! I’ll be using Python in the examples in this post, but this is not a Python-specific issue. This has to do with how computers represent numbers internally.

Not quite $999.997634 but you get the point.

Rounding errors

The reason issues like the one in the code above occur is due to rounding errors when doing floating point arithmetic. Let’s repeat the example from the video above. Here we’re adding just two floating point numbers:

Why 0.30000000000000004? You should go back and watch the video above :)

And this doesn’t only happen with addition. Let’s say we want to calculate the proceeds from selling 165 of XYZ stock at $19.40:

If you have these many 9s in availability, I’m switching to your service yesterday.

If in either of these two examples you pull up your favorite calculator, you will get the “correct” answer, i.e. 0.1 + 0.2 = 0.3 and 165 * 19.4 = 3201.

Hopefully, by now I should have convinced you that using float to store money is a bad idea. If you’re still not convinced, keep reading!

Listen to Mr Mackey. He’s an expert on IEEE 854–1987.

Always use integers to store money

While float is the troubled child in the family, integer is the poster child that always behaves as it’s expected of her. In our case: no rounding errors.

How does this work? What about cents?

I think the primary reason we automatically use floats to represent money is that we more commonly think in a currency’s main unit, e.g. 19.4 dollars.

But forget what Diddy and Biggie told you. It’s all about the cents.

To avoid rounding errors, you should store money in a currency’s smallest unit using an integer.

For example, we’d represent 19.4 dollars as 1940 cents. Just make sure you use long integers, so you don’t run into brand new problems.

Storing money in a currency’s smallest unit is a common software design pattern made popular by Martin Fowler back in 2002. It’s even adopted by Stripe in their APIs. That’s a pretty solid endorsement in my books.

Let’s retry the code for the example above using this pattern:

Hurray! No overflow when multiplying integers.

Note that now that every monetary value is in cents, we need to be aware of that and properly format stored values into what the user expects to see.

At minimum, we need to divide the stored value by 100:

>>> 320100 / 100
3201.0

This is what we would expect as a result if we’d done the math by hand.

What’s better than an integer? A Money library.

As we saw above, it may be easy to forget that monetary values are in cents and run into new problems in the presentation layer/UI. The best way to deal with this is to use a dedicated library (or class) for everything money-related.

There are ton of great libraries that implement this pattern: Dinero.js is a really great one for JavaScript as is Money for Elixir (the two main languages we use at AgentRisk). Surprisingly, I wasn’t able to find something for Python.

The most common one for Python is good, but it uses Decimal to store values.

That’s why I decided to write my own and make it available on GitHub. You can check it out here: https://github.com/agentrisk/agentrisk-money.

It’s still in beta, but has decent test coverage. PRs welcome!

Thanks for reading!

Hopefully this was useful to you, or at least a good refresher how floating point arithmetic works. If you want to nerd out further, I’d recommend going deep in this classic article from ACM. Mind you, there are other issues one can run into when handling money, like allocation and calculating percentages. Both these will be added to agentrisk-money library very soon.

At AgentRisk, we build investment products and tools for individuals as well as for financial advisors and wealth managers, leveraging state-of-the-art machine learning algorithms and time-tested investment theories.

--

--