Built in datatype for handling money values?

Except it is way trivial to accidentally handle, say, a remainder on a division then, that’s the major issue.

You should never use a basic number as a money type, ever ever I would assert.

Whether you use ex_money or make your own, never allow the base monetary value to be altered directly.

+1

3 Likes

I have since written this project using Decimal Library …, I just shake my head when i look at the source… it really looks like over-kill

I get where you are coming from, what happens if we have > 2-decimals for ANY reason … for example splitting 0.33

1 Like

I agree with what other people have already said. The problem with a concept like ‘money’ is that it is a very vague one: What exactly you need really depends on your application:

  • Do you need to handle multiple currencies, or only one currency?
  • What rules for decimal places and rounding (half up, half even?) do these currency(/ies) follow?
  • What kind of operations make sense for your domain? Usually it is non-sensical to multiply two monetary values together, but maybe it’s not in your domain?
  • If your domain is about accruing small rewards over a long period of time, you very well might need more decimals inside your calculations before converting to a result with less precision at the very end.

If I’d find myself in this situation, I would probably create a custom struct wrapping a fixed-point number, and indeed implemented Numbersadd, sub abs and to_float, as well as mult and div where one of the two operands (in the div case: the divisor) is expected to be an integer or float; passing two money-values to these will raise.

1 Like

You don’t round money, you get back a result of the operation and the leftovers so you have to handle them somehow. This is one of the big no-no’s of money systems. You Don’t Round Money.

1 Like

So how do you split 0.33 two ways and record that in a system that has cents (2-decimal) as its smallest REAL currency?

Just use cents or the smallest currency item as an integer and go from there.

So, store $10.50 in your db as 1050.

Say you want to divide it in half, then your only option is to return two 0.16’s and 1 0.01. Then you’d probably just add the 0.01 to one of them to make it 0.16 and 0.17, or put it somewhere else.

Yeah I’d always store it as an integer of the smallest representable monetary amount too.

2 Likes

Money is complicated. Splitting an amount of money is a good case. And indeed there are cases for rounding as well.

  1. The current exchange rate today for USD -> JPY is 111.869. I have USD 24.39 and when converted I end up with JPY 2728.9971

  2. This is a totally valid accounting value (I understand most fintech systems require at least 7 digits of precision but I can’t site a reference for that). But of course eventually we have to put an amount in a customer account that is a settlement value that is a cash amount. Rounding JPY means no decimal digits so the result would be (using the default “half-even” or “bankers rounding” is JPY 2729.

  3. Online services can charge at fractional cents. For example AWS charges $0.192 per Hour for a m5.xlarge instance. Rounding is required prior to billing

  4. Splitting definitely has its own challenges. Splitting a money amount and rounding to the appropriate digits for a currency can leave a balance that shouldn’t be lost. I treat this in ex_money by returning the split amount and the remainder:

iex> Money.split(Money.new!(:USD, "12.43"), 3)
{#Money<:USD, 4.14>, #Money<:USD, 0.01>}

In short, there are valid reasons why a money amount may have more precision that the currency’s definition since intermediate results can create such scenarios. Currency conversion is one good example, interest rate calculations is another, stock trading another one. Eventually to settle accounts rounding will be required and management of the remainder will then also become important.

6 Likes

Except that 20 years ago the maximal number of decimals was 5 and now (thanks to ₿) it’s 8. I could not imagine the horror uglier than updating the existing codebase to support bitcoins now.

1 Like

Each monetary value would have a different base amount regardless when storing it in a database, just shift the decimal to equal the base-most amount that can reasonably be handled, which I’d put at a lot more than 8 for bitcoin all things considering.

1 Like