We are the Dev Teams of
  • brands
  • ebay_main
  • ebay
  • mobile
<
>
BLOG

Money is...

by Robert Kosten
in My Two Cents

…not a floating point value

If you’re not familiar with the common pitfalls of floating point arithmetic you may think that money can be represented in ways similar to

$amount = 0.1;

for 10 cents. It looks intuitive, right? After all, that is how most of our programming languages (in this article I’m going to focus on PHP, because a surprising amount amount of software dealing with money on the Internet is written in it and that need not be a bad thing) deal with decimals and it seems to work most of the time. Sadly, it will cause a lot of hard to debug problems down the road, once you run into enough calculations to encounter the corner cases and the accounting department comes with a stack of off-by-one-cent issues. You’ll find this in more legacy code than anyone cares to think about. And these corner cases are not as rare as you might think either, have a look at this real-life example:

<?php
$floatTotal = 19.99 + 39.99;
print ($floatTotal - 59.98);

You’d expect that to print

0

Right? Wrong! If you run that snippet of code you’ll discover it actually prints

7.105427357601E-15

a value that is close to, but most certainly not equal to the first and your

if (0 == $float) {

just failed in a way that will take you hours to track down. This gets especially tedious when you’re trying to distribute a given amount among several buckets and you inevitably end up losing cents in the process unless you have a well-defined allocation strategy.

Thankfully all is not lost. The easiest solution is to always calculate in the smallest denomination and just move the decimal point in the presentation layer. This is straight forward though a little weird when looking at debugging data. The alternative is to use arbitrary precision libraries, like bcmath in PHP or BigDecimal in Java.

<?php
bcscale(5); // Truncate at 5 decimal places
$bcTotal = bcadd('19.99', '39.99');
print(bcsub($bcTotal, '59.98'));

Even these approaches will fail if you have to handle non-decimal currencies, thankfully the only two still in use have sub-denomination values so low that no coins are produced for them and you can probably ignore them. But it would certainly seem like a good idea the encapsulate your money in an object, so that support at least becomes possible. After all, the British Pound Sterling was only decimalized in 1971 and who is to say that no major currency will change the other way while your code still runs?

…not without Currency

Another problem cropping up especially in code bases used in smaller shops and start-ups is missing currencies, which inevitably becomes a problem once they go international. As long as all your calculations are based in USD or EUR it is no problem to ignore the currency, but adding it into the code when your business branches out to, say, the United Kingdom (Pound Sterling), Switzerland (Swiss franc) or Costa Rica (Colón), you’ll run into issues with storing values in different currencies in the same database. And you’ll still be required to generate reports over all those transactions.

What happens when you add up 3 USD and 2.20 EUR? Are they equal? Today they are, in some markets, but tomorrow? Are 0 USD and 0 EUR equal? How do they compare when ordering? These are questions that your business needs to answer.

If some reports need to total different currencies then a separate ConversionRateService is probably the best idea, so that changing the currency of a money object has to be done deliberately.

…a ValueObject

Money objects should be immutable and follow value semantics to avoid side effects and make them thread-safe efficiently (In languages with threads, of course). It should be as difficult as possible to change a money object that has been set somewhere (for example in an accounting record). In short, they need to be ValueObjects.

Putting it all together: The Money Pattern

Sadly, very few languages and frameworks provide a solution for money out of the box, but a well-established pattern is available and reading all of the Patterns of Enterprise Application Architecture is highly recommended to anyone building complex software that might one day grow into a system that someone else has to debug, no matter the language it is written in. It provides a strict guideline on how to implement arithmetic, comparison, equality and allocation and a very small handful of more or less complete implementations for PHP have cropped up within the last couple of months:

programming, php, design pattern

?>