Built in datatype for handling money values?

It would be nice to have a built in datatype for handling money values.

Most currency activities are exactly two decimal places.

Such a built-in convenience would be more helpful that requiring the decimal library, for example since all we need is 100.00 or 2-decimal place accuracy at most.

Please what suggestions do you have? or should we just be using floats for this, since the required precision is exactly 2 decimal places.

1 Like

[money] (https://hex.pm/packages/money) does exactly what you want. It is not part of the standard library because it does not need to be. This is actually a great advantage because now it can be updated independently.

3 Likes

Enum.reduce(data, 0, &sum.(&1, &2))

I’m pulling data from a database and cannot know ahead of time if all my data would be Money types, but i just need to perform very simple “math” on the data that pull from the db

The code sample above fails when using Decimal: ERR: %ArithmeticError{message: "bad argument in arithmetic expression"}

In that case I’d advise you to use Numbers, which is made to solve exactly that problem.

Do note that Money does not follow Numbers’ specification completely right now, though (And adding that would probably be a backwards-incompatible change).

Another approach would be to define a summation protocol and implement this for any datatypes you might want to use.

This package is probably better, and defines money datatypes for databases (currently postgresql and mysql, I think):

According to the data I have there are:

  • 52 currencies that have no decimal places
  • 239 that have 2 decimal places
  • 3 that have 3 decimal places
  • 1 that has four decimal places

But you should also consider that some currencies have specific rounding rules for cash values that relate to the physical currency in circulation.

For example, the AUD and CHF have cash rounding to 0.05 but normal rounding to 0.01.

Additionally there is the question as to when to round and to what precision. In a chain of financial transactions it is most common (according to my research, not an formal statement) that rounding is performed only at the end of the chain.

Then if you are splitting up money (like allocating it to various amounts) then rounding has to be handled carefully to ensure that any remainder is taken care of.

Anyway, money handling and its implication in the real world is an interesting problem - but its not just about decimal arithmetic (I am the author of ex_money and suggestions and PRs are always welcome)

6 Likes

thanks for the stats, but see where the trouble lies … I feel Elixir should make handling decimals natural, so we can do stuff like 100.00 + 10 … and it should just work

Using a protocol, say via @Qqwy’s number library, you could easily just redefine + to call it’s protocol dispatched add function, then it would just work with, say, Decimal.

Something like 100.00 + 10 is ill defined though, I hate hate hate it when languages allow it. Do you want it to be 110.00 or do you want it to be 110 or do you want it to come out as some new type or etc… etc… That is why I like statically strongly typed languages making both operands to + be the same type, that way it is properly formed.

7 Likes

From a decimal and money point of view, how would you want it to manage 100.00 / 3 ? The issues of rounding, when to round, what kind of rounding all still need to be managed and the defaults are probably not what you want and expect.

Rounding is often done in the “half up” fashion - as it is for floats in Elixir/erlang, for financial transactions you probably want to use “half even”.

If you do (100.00 / 3) * 3.2351 then when should the rounding be applied? At each step or only at the end? Where would you specify the rounding in such an expression?

I’m not sure a language can apply the necessary convenience and yet be transparent on the underlying mechanisms. And given that Elixir favours being explicit over implicit I suspect this isn’t a likely focus for the core team.

3 Likes

Understood. But for day to day usage, how best should we handle 2-decimal exact precision numbers? If I’m working with money, then I know that 100.00 +10 is ALWAYS 110.00 - I have a context, I’m working with MONEY

You should convert everything to money before adding. The question of 100.00 + 10 shouldn’t even come up

4 Likes

100.00 + 10 == 110.00 is only true if they are both of the same currency. Its money, so the details are important.

As @tmbb suggests, picking a money lib and convert everything to money first so you know you’re dealing with a currency would be a good recommendation.

6 Likes

This is the data you provided in your previous post. Your output gives you an error because you are expecting some kind of number, but you have a date in the “maturity-date” and a string in the “description” field. I’m not sure if this is intended or not, but it is not a number, so you should not be iterating over it as if it were.

1 Like

i’m testing for the field type before adding. I have solved it, there was an error is my example. thanks

see this answer:

In most cases it would not make any sense to speak of fractional cents…

like you have correctly listed,

52 currencies that have no decimal places
239 that have 2 decimal places
3 that have 3 decimal places
1 that has four decimal places

Pre-setting the number of decimal digits, then scaling all values to integer units, should be a big win for simplicity as well as performance.

2 Likes

I know its an older post but I wanted to observe that what is described by @CharlesO is basically what the Decimal package does. It’s a lovely piece of code (as you’d expect from @ericmj). It’s hard to consider using anything other than a decimal format for money types (the representation under the covers matters less - but Decimal is very tightly optimised). And as a lib writer, I find it hard to consider a money type that isn’t tagged in some way with a currency type. There’s just too many things to go wrong.

2 Likes

Decimal is good as the underlying type, but it doesn’t enforce handling partial unrepresentable money values, or verifying the same type of money is being compared/added/etc… That’s why the ex_money library is useful.

If you are responsible for a system that will only ever have 2 decimal point numbers and the operations involved are simply addition, subtraction, hardly ever multiplication or division, I still feel there might be some simplifications that may be obtained by not taking on a dependency.

In such a case a simplification can indeed be made, but it’s a project specific simplification and non e.g. a library or even more the core should make. The best case is always that once code is written you can change the currency without touching any of the logic working on the values. Your constraints allow simplifications, but also limit the selection of currencies to be usable. Especially the core (when we’re talking about build in stuff) should not arbitrarily limit the selection of currencies, especially if it’s just because it’s simpler to implement. If you write a library, which does so, it’s fine for as long as you communicate those limitations properly. And on a per project basis you can do what ever you want anyways. On the per project basis I would still suggest wrapping money values in a struct, which is documented to work within the given constraints and add an api to handle the manipulations on the value. If all you do is add and subtract stuff, such a module should be written quite quickly and you can skip external dependencies.

2 Likes

One should not try to build malformed partial implementations in any case, but even if all the currencies were having 2 decimals, the money unlike numbers require an ability to split 0.33 into two shares, than join them back and get 0.33. Otherwise the first audit would dismiss the whole business. That is impossible without very careful implementation, that is IMHO harder than anything else in the field.

5 Likes