Math with Money, How to Avoid Death by a Thousand Cuts

André Cohen |
2019-05-08

Math with Money, How to Avoid Death by a Thousand Cuts

Every piece of data that a developer interacts with needs to have an appropriate data type. Broadly speaking, the data type tells the programming language how the developer intends to use a piece of data. Common data types like ‘string’ are used to represent pieces of text, and marking something as a string allows the programming language to understand that when you add two strings,you actually mean to concatenate the strings together. Similarly, when you tell a programming language that something is a large number with many decimal places, certain functionality and restrictions are automatically provided to the developer. For example, adding numbers together but preventing a number and a string from being added together.

This generally works well for most programming problems. However it breaks down when dealing with money. At first, money appears to be just a number with two decimal places, but if you think a bit longer you realize that the currency (USD, EUR, etc.) is just as important for the meaning of the data. The currency is practically as important as the + or - that comes in front of a number. Also notice how exchange rates break the “2 decimal places” rule. This is the tip of the iceberg of why money doesn’t behave like the ordinary number.

Floats do not Represent Money

Floats and doubles are two common data types used by developers to represent money in a programming language. It appears perfect because they can store very large numbers and handle fractional amounts. But, if you peak under the hood of what a float (or a double) is, you realize that float numbers are actually storing approximations of numbers. In Python for example:

1.0/10.0 = 0.10000000000000001

Which appears pretty harmless, but this error quickly escalates as you add further mathematical operations to that number. When you begin to tally up the total revenue of a game these small approximations (that often don’t show up in a dashboard because of UX rounding) lead to surprisingly large deviations.

The solution is simple: when dealing with money, you should stick to using numeric representations that contain no fractions. Integers and decimals are best for money, representing a dollar amount in cents.

Money Type

Even when representing money in the form of an integer, it's important to keep track of the currency as well. If the currency is not explicitly attached to the amount, I guarantee currencies will be mixed up down the road and Euros and Dollars will be added up together.

Creating (or using) a library that manages money as a proper data type will give you a few safety nets:

  1. It will manage decimal representation of money (an important layer to making sure the internal 250 cents amount doesn’t mistakenly get interpreted as $250).
  2. It will ensure money type is immutable (https://en.wikipedia.org/wiki/Immutable_object) to prevent a value from being modified in one part of your code and having unintended consequences in other parts of your code.
  3. It will overload math operations to ensure that incorrect currency comparisons raise errors.

Only Convert at the End

Inevitably a currency amount will have to be converted to a base currency. At Gondola, for example, we always use USD in our reports, but this conversion should be left to the very last minute. This means platform fees and taxes are all applied on top of the original local currency amount. Furthermore, when adding up the revenue from all in-app purchases, it is best to add all like currency amounts first, and then, at the end convert all currency subtotals to USD. This reduces rounding errors and keeps results predictable.

Conclusion

It only takes one round of data validation to spot the need for a money type in any financial-based program. It only takes one bug that causes currencies to be assumed as USD (when they are in fact not), or a basic rounding error to generate an appreciable error at the end, to recognize that a money type can solve these issues before they begin. When validating monetary data, it’s also important to double check the ground truth since it is likely to have interesting bugs as well. Think of all analytics service providers, and ask yourself, “how do they deal with monetary metrics?” The answer is they don’t!. Writing a money type is actually a rather fun task and will give you a new appreciation for accuracy.