Reputation: 2331
I have Elixir calculations returning floats like this:
0.15300000000000002
5.140000000000005
0.0033000000000000004
But I only want to display:
0.153
5.14
0.0033
I'm familiar with using...
:erlang.float_to_binary
...to specify decimal precision & rounding options. The problem is I don't know exactly the decimal place to set precision at before hand.
Is there a simply way to do this?
Upvotes: 1
Views: 1438
Reputation: 4850
1) One possibility is to round the number with slowly increasing precision until you are within a tolerance that you specify. For example
defmodule Foo do
def foo(num, tolerance, precision) do
x = Float.round(num, precision)
if num + tolerance > x and x > num - tolerance do
x
else
foo(num, tolerance, precision + 1)
end
end
end
num = 0.15300000000000002
tolerance = 0.00000000001
Foo.foo(num, tolerance, 0) == 0.153
2) One possibility is to shift all the numbers to integers before doing the calculation and then shift them back to float afterward. Elixir uses bignum arithmetic so it shouldn't have any issue doing this. For example, the following gives you an imprecise result with a lot of 9s at the end.
iex(1)> 234.987 * 4085.984
960153.1222079999
But, this one is more precise. Just choose some amount sufficiently large to shift by.
iex(2)> (234.987 * 1_000_000) * (4085.984 * 1_000_000) / 1_000_000_000_000
960153.122208
To go even a step further, in a lot of systems that I've built, we simply stop using floats whenever possible because of the issue with precision. For example, when storing and dealing with currency such as USD, use amount_cents instead of amount or 100 for $1 instead of 1.0.
Upvotes: 0
Reputation: 4885
Due to the nature of floating point error, I don't think there is a general way to do this without choosing a precision.
the :compact
option to float_to_binary
can help:
iex(10)> :erlang.float_to_binary(1.0 / 10.0, [{:decimals, 15}, :compact])
"0.1"
iex(11)> :erlang.float_to_binary(1.0 / 10.0, [{:decimals, 30}, :compact])
"0.100000000000000005551115123126"
It trims trailing zeros up to the precision you specify.
Upvotes: 5