What are the best practices for storing currency/monetary values in the DB, processing them in a server-side app, and finally sending them down to the browser via a JSON API?
I've figured two approaches, but I'm not sure how to weigh out the pros & cons:
BigDecimal
in Ruby.Is there a generally accepted best practice for this?
There are two additional gotchas for both practices.
Firstly, not all currencies need two decimals. Many need three. Many more need zero. And some field-specific apps, e.g. finance and forex, need 5 or 6.
Secondly, some currencies have colorful conversion rates for subdenominations. Old-school rupees, for instance, converted to 16 annas, 64 paise or 192 pies. Luckily, only two countries retain this kind of crazy conversions rates if wikipedia is anything to go by -- Mauritania where 1 ouguiya = 5 khoums, and Madagascar where 1 ariary = 5 iraimbilanja.
http://en.wikipedia.org/wiki/Decimalisation
The point, though, is that you shouldn't be too US-centric in your assumptions if you plan to ever localize your app. At the very least, consider the use-cases where you've 0, 2 and 3 decimals, and chew a bit on what path you'd want to take if you ever went international. Just in case.
As an aside, also note that the numeric
type in Postgres can be stored without specifying a precision. It takes no more room to do so, since it'll reside in the extended storage when too large just the same. And, much like varchar
is better than varchar(n)
, it'll be faster because you'll skip the built-in precision check when storing your numbers (which, in this case, needs to be sane indeed).
As for which of the two approaches that you describe is best, I've seen more of the second -- in a simplified form.
I'd stress three points:
Storing currency as numeric
, with or without a precision, makes sense in the DB, for the sake of not introducing rounding errors when generating accounts and reports. (There is a built-in money type, too, but I've never it seen used in practice for all sorts of very valid reasons.)
Speed should be the least of your worries if you actually need the kind of precision that overflows a bigint or a double-precision float (the latter of which is used by js and json for floating point numbers, if memory serves). If this is your case, arbitrary precision maths is the way to go, from one end to the other.
Typical apps that manipulate typical currency amounts will never run into these overflow limits. Take a cold hard look here. Unless you're planning to store amounts in quadrillions of US dollars, converting unbounded numerics to string and back to pass them around in json is a sign that you're over-engineering your app. If this is your case, just stick to double-precision floats and ship your app.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With