How to escape scientific notation

Hi all,
I just learned elixir , I want to ask how to escape scientific notation in Math operation

Elixir 1.7.3

iex> 1000 + ( 1000 * 0.11)
1110.0

iex> 1000 + ( 1000 * 0.1)
1.1e3 // I expect 1100.0

iex> 1000 * 0.1
100.0

iex> :erlang.float_to_binary(1000 +(1000 * 0.1), [:compact, { :decimals, 20 }])
“1100.0” // got String type of 1100.0

can somebody explain it ? is this normal behavior ?

I just want to add 10% of original_value with original_value

thx

1 Like

1100.0 and 1.1e3 are equivalent and equal:

iex(2)> 1.1e3 === 1100.0
true
iex(3)> 1.1e3 == 1100.0
true

So I’m not sure what you are worrying about?

If it is just the representation, then use a function of your choice to format for printing, but keep it as a float as long as you can.

1 Like

iex> 100 * (1 + 0.1)
110.00000000000001

iex> 100 * (1 + 0.2)
120.0

I’m little confused here

Why? Thats as floats are supposed to work. They often can not represent exact (decimal) numbers.

There are a lot of blog posts and even papers out there in the wild, that will tell you not to use floats for anything but OpenGL.

1 Like

Ok , I should read document from “h Float” first

There are some very well known problems with floating-point numbers and
arithmetics due to the fact most decimal fractions cannot be represented by a
floating-point binary and most operations are not exact, but operate on
approximations. Those issues are not specific to Elixir, they are a property of
floating point representation itself.

For example, the numbers 0.1 and 0.01 are two of them, what means the result of
squaring 0.1 does not give 0.01 neither the closest representable. Here is what
happens in this case:

• The closest representable number to 0.1 is 0.1000000014
• The closest representable number to 0.01 is 0.0099999997
• Doing 0.1 * 0.1 should return 0.01, but because 0.1 is actually
0.1000000014, the result is 0.010000000000000002, and because this is not
the closest representable number to 0.01, you’ll get the wrong result for
this operation

There are also other known problems like flooring or rounding numbers. See
round/2 and floor/2 for more details about them.

To learn more about floating-point arithmetic visit:

0.30000000000000004.com (http://0.30000000000000004.com/)
• What Every Programmer Should Know About Floating-Point Arithmetic
(http://floating-point-gui.de/)

,
thx @NobbZ

1 Like

consider using Decimal if lack of precision is an issue… or you need to control it…

2 Likes

Also in case these are amounts of money - don’t use floats :slight_smile: Take a look at the Money implementation in Elixir https://github.com/liuggio/money

5 Likes

thx @outlog , @yurko for the information

1 Like

For future folks…I have converted floating-point numbers to strings up to 3 decimal places with the following function:
:erlang.float_to_binary(float, [{:decimals, 3}, :compact])

It will provide floating numbers as a string without scientific notation. And helper function as follows:

  def float_string(float, decimals) when is_float(float) do
    s = :erlang.float_to_binary(float, [{:decimals, decimals}, :compact])
    Regex.replace(~r/\.0\z/, s, "")
  end

You can get printf-like formatting with the :io_lib.format function.

iex(1)> :io_lib.format("~.2f", [232.2389])
~c"232.24"
2 Likes

Just make sure that the number you are passing in actually makes sense.

For example:

iex(1)> big_float = 100_000_000_000_000.123
100000000000000.12
iex(2)> Masudme09.float_string(big_float, 3)
"100000000000000.125"
iex(3)> :io_lib.format("~.2f", [big_float]) |> to_string
"100000000000000.13"

And another one:

iex(4)> weird = 36028797018963971.0
3.602879701896397e16
iex(5)> Masudme09.float_string(weird, 3)
"36028797018963968"
iex(6)> :io_lib.format("~.2f", [weird]) |> to_string
"36028797018963968.00"

Here the scientific notation is actually hinting that you are losing precision.

2 Likes

Thanks @adamu. Great stuff.

I see this:

but because 0.1 is actually 0.1000000014

Then I try this:

iex(4)> 0.1 == 0.1000000014
false

What is going on?

That’s true for single-precision floats, but the beam uses double-precision, so it’s actually:

iex> 0.1 == 0.100000000000000005551
true

(actually even for single-precision floats, apparently the value stored for 0.1 is really 0.100000001490116119384765625)

edit: I pulled that 0.100000000000000005551 value from a @kwando’s function above, but in fact any decimals added after 0.10000000000000001 will produce the same float. I suspect that :io_lib.format doesn’t give us the full accurate decimal representation, probably because the precision is meaningless as any other number nearby would also be represented as the same value.

edit2: According to this the actual value of 0.1 is 0.1000000000000000055511151231257827021181583404541015625.

Also this is a nice quote from the same article (I changed the number to 0.1 for clarity):

So when you print out that number, why does it display 0.1?

The computer isn’t actually printing out the exact value of the number, instead it’s printing out the shortest decimal number d which has the property that our floating point number f is the closest floating point number to d.

1 Like

Very interesting, thank you!