Up until 4-5 years ago, the EUR/USD or GBP/USD or GBP/EUR were quoted with 4 decimals; Nowadays, there are quite a few places where you have to deal with the 5th decimal to make the books balance. It's going to be a roundoff error either way, but it might be a round-off error that keeps been counters, auditors and therefore eventually IT people awake at night.
And then the market adopts a 5th digit - and you have to essentially rewrite all the math stuff.
If you're using language support (e.g., in VB until 2004 "dim cost as Money" was a declaration that was good for every purpose), then you're out of luck, and you do need a rewrite.
But even if you don't - this is far from trivial: Is your own implemented money type a fixed point type? If so, what is your fixed point? (e.g. Yen only needs 3 after the dot, but trillions in the front is not enough; GBP/USD needs 5 after the dot, but trillions usually IS enough).
Is it fixed per currency? Is it floating decimal point? The abstraction here is far from trivial if care about performance.
Of course, if you've used a language or a library with a usable decimal type (I'm sure there's other, but I've only been happy with Python), the abstraction has been taken care of.
But generally, abstracting money (value+currency) is not as simple as one would assume, and it is very rare that a production system gets it both right and future proof.
At that point screw it, 64.32 for 18.9 digits and round it to 128 bits with a currency code. Comes with built-in unit checking.
But it works well enough.
(Money 3 * 2 will return Money 6 if fromIntegral 2 = Money 2. But that is not really the operation you want to perform, so you might consider not implementing Num for Money.)
1. The speed gain is negligible for most programs
2. The addition of any pricing that requires fractional cents will require careful work to handle unit conversion to maintain integral representation.
#2 becomes ugly when one has an API many people use, and one must bother them to update their code paths to use the new, higher precision that can handle all money as integers. This is not even counting the case where one tries to cut a corner and someone ends up not doing the conversion they ought to.
Also, programs often are more lucid when operating in terms of the frequent units of choice, such as dollars or fractional dollars. Few domains price everything in cents by preference, because in aggregation often dollars -- sometimes many -- are exchanged. The problem gets worse if one needs weird units like 0.1c to regain integral numbers.
There are ways around these, but falling back on strings seems to me the lowest-maintenance option.
On the other hand, integral representations have few dependencies (a compliant javascript interpreter), which is also a pretty big plus for that.
Just about every practically used programming environment from every walk of life has such a thing in the standard library, and I suppose they are there for good reasons. However, the fact is that Javascript doesn't have one, so one will have to weigh dependencies into their decision.
Go - math/big
Java - BigDecimal
Python - decimal
Ruby - BigDecimal
PostgreSQL - numeric
Interesting mention: decimal in C#, deemed sufficient for monetary calculations at 128 bits of precision.
Another interesting mention: Oracle, which in my understanding handles just about all its numbers this way, by default. This might tell you something about their early customer base.
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
Math.pow(2, 53) === Math.pow(2, 53) - 1 // false
But I wonder why you write that for values in 1/100 one should multiply by 100! Wouldn't an IEEE double have no problems with values with two decimal points, too? Or are there many problems with that, e.g. that $0.01 + $0.01 might already cause rounding errors which do not even out by the clever IEEE rules?
Of course 2^54 woudln't be the highest value where one shouldn't worry anymore, but something smaller.
I only mention it because the yen thing surprised me once as a newbie programmer. (A tip: "%0.2f" is no substitute for actual i18n.) It never hurts to be aware of the edge cases.
I'm afraid it mine can only handle sea shells!
>I only mention it because the yen thing surprised me once as a newbie programmer.
So what's the yen thing? How does it differ?
[A bit of googling also turns up the "rin" (厘), which is a thousandth of a yen...]
The idea is that you have income for each loans, which then pay into bonds based on whatever rules may have been set. These rules often have terms like a fixed interest on the outstanding principal for senior bond issues, and then various divisions for the junior, with a weird "IO" piece that gets the leftovers that don't divide neatly. The rules can be anything that they were structured to be. The result surprisingly frequently is something where the allocation of the final penny in billions of dollars can be impacted by floating point ambiguity. (And the prospectus seldom will clarify this - the ultimate control lies in whatever the servicer's computer program does.)
EDIT: Millage taxes on property are denoted in mills—thousandths. The taxes are often fractional mills, like 2.225. So you need 10^-8 resulution at the least.