As a note for posterity: there is a material cost to processing options for Cldr.Numbers.to_string/3 which is ultimately what Money.to_string/2 calls. It is possible to pre-validate these options for exactly the case that the original post is motivated by: tight loops.
With pre-processing the options the time is further improved.
- Original case (bug in
Cldr.validate_number_system/1: 2.99 ms - Bug fixed in
Cldrversion 2.7.1: 111.34 μs - Using pre-validated options: 57.51 μs
A speed up of 50x over the original case.
Performance comparisons
Version Name ips average deviation median 99th %
Original with bug:
2.7.0 to_string 334.14 2.99 ms ±24.62% 2.79 ms 5.41 ms
Bug fixed:
2.7.1 to_string 8.98 K 111.34 μs ±29.45% 102 μs 224 μs
Pre-validated options:
2.7.1 to_string 17.39 K 57.51 μs ±35.82% 50 μs 125 μs
I published ex_money version 3.4.4 to surface this optimisation which can be used as follows:
Using pre-validated options
iex> money = Money.new(:USD, 100)
# Apply any options required as a keyword list
# Money will take care of managing the `:currency` option
iex> options = []
iex> {:ok, options} = Cldr.Number.Format.Options.validate_options(0, backend, options)
iex> Money.to_string(money, options)
The 0 in validate_options is used to determine the sign of the amount because that can influence formatting - for example the accounting format often uses (1234) as its format. If you know your amounts are always positive, just use 0.
If the use case may have both positive and negative amounts, generate two option sets (one with the positive number and one with the negative). Then use the appropriate option set. For example:
iex> money = Money.new(:USD, 1234)
iex> options = []
iex> {:ok, positive_options} = Cldr.Number.Format.Options.validate_options(0, backend, options)
iex> {:ok, negative_options} = Cldr.Number.Format.Options.validate_options(-1, backend, options)
iex> if Money.cmp(money, Money.zero(:USD)) == :gt do
...> Money.to_string(money, positive_options)
...> else
...> Money.to_string(money, negative_options)
...> end
Updating dependencies
Simple as:
mix deps.update ex_cldr ex_money






















