You better work in cents, not dollars 💸
Tags: python, elixir
Published: 2020-02-23 21:56:31

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:

{% youtube PZRI1IfStY0 %}

# Why using float to store money is a terrible idea

I couldn’t have said it better than [Bill Karwin](https://twitter.com/billkarwin):

{% twitter 347561901460447232 %}

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.

{% gist https://gist.github.com/aloukissas/bcebba7de7b1c4b5e0d2008ebfa80f6e %}

## 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:

{% gist https://gist.github.com/aloukissas/bb2b0a153a8d927e6cdba2be75a6d922 %}

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!

![mrmackey](https://i.imgur.com/xOpOgnu.jpg)

---

# 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](https://en.wikipedia.org/wiki/Integer_overflow).

Storing money in a currency’s smallest unit is a common software design pattern made popular by [Martin Fowler](https://martinfowler.com/eaaCatalog/money.html) back in 2002. It’s even adopted by [Stripe in their APIs](https://stripe.com/docs/currencies#zero-decimal). That’s a pretty solid endorsement in my book.

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

{% gist https://gist.github.com/aloukissas/804fd008cb94dc378ede630f43ccf28b %}

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.

![lisa](https://media.giphy.com/media/xT5LMQRPHbTymgc5Bm/giphy.gif)

---

# 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](https://sarahdayan.github.io/dinero.js/) is a really great one for JavaScript as is [Money](https://github.com/elixirmoney/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](https://github.com/carlospalol/money) for Python is good, but it uses `Decimal` to store values. While this is generally fine, we really wanted to stick with the Fowler pattern across all systems and languages and use integers to represent money everywhere.

That’s why I decided to write my own and make it available on GitHub. You can check it out here: {% github 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](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) from ACM. Mind you, there are other issues one can run into when handling money, like [allocation](https://sarahdayan.github.io/dinero.js/module-Dinero.html#~allocate) and calculating [percentages](https://sarahdayan.github.io/dinero.js/module-Dinero.html#~percentage). Both these will be added to `agentrisk-money` library very soon.

---

_This article was originally posted on the [AgentRisk blog](https://blog.agentrisk.com/you-better-work-in-cents-not-dollars-2edb52cdf308)._