Emily
Emily

Reputation: 2331

How To Format A Number to Precision in Elixir?

What is the most direct, & efficient way to do this in Elixir?

Starting number: 123.101

Ending number: 123.101000 # Adding 3 digits to the precision of a float.

Starting number: 123

Ending number: 123.000 # Adding 3 digits to the precision of an integer.

Starting number: 123.101

Ending number: 123.1 # removing precision

Starting number: 123.000

Ending number: 123 # removing precision

Upvotes: 27

Views: 19740

Answers (5)

Madasebrof
Madasebrof

Reputation: 303

These answers are mostly odd. First, she didn't say anything about converting to a string. Second, there is certainly no need to jump into Erlang land!

In order:

Starting number: 123.101

Ending number: 123.101000 # Adding 3 digits to the precision of a float.

iex> 123.101 |> Decimal.from_float() |> Decimal.round(6)
#Decimal<123.101000>


Starting number: 123

Ending number: 123.000 # Adding 3 digits to the precision of an integer.

iex> 123 |> Decimal.from_float() |> Decimal.round(3)    
#Decimal<123.000>


Starting number: 123.101

Ending number: 123.1 # removing precision

iex> 123.101 |> Decimal.from_float() |> Decimal.round(1)
#Decimal<123.1>


Starting number: "123.000" # must be a string as 123.000 == 123.0

Ending number: 123 # removing precision

iex> "123.000" |> Decimal.new() |> Decimal.round(0)
#Decimal<123>

Once you have a number in Decimal, you can do what you want, e.g. convert to a float, convert to a string, etc.

iex> 123.101 |> Decimal.new() |> Decimal.round(6) |> Decimal.to_string()
"123.101000"

iex> 123.101 |> Decimal.new() |> Decimal.round(6) |> Decimal.to_float() 
123.101

iex> 123.101 |> Decimal.new() |> Decimal.round(0) |> Decimal.to_integer()
123

As an aside, I recommend working only in Decimal until you've finished any data manipulation, e.g. using Decimal.mult(num1, num2), Decimal.div(num1, num2), etc. Decimal rocks!

Upvotes: 10

MartinElvar
MartinElvar

Reputation: 5804

Just want to supply a alternative to Dogbert's excellent answer.

It is also possible to use :erlang.float_to_binary/2

ex.

iex(5)> :erlang.float_to_binary(123.101, [decimals: 6])
"123.101000"

iex(6)> :erlang.float_to_binary(123.0, [decimals: 3])
"123.000"

iex(7)> :erlang.float_to_binary(123.101, [decimals: 1])
"123.1"

iex(8)> :erlang.float_to_binary(123.000, [decimals: 0])
"123"

Upvotes: 42

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Just out of curiosity, that’s how I would implement in in pure Elixir:

defmodule NumFmt do
  def format(value, pos, round? \\ true)
  def format(value, pos, _) when is_integer(value),
    do: format(Integer.to_string(value), pos)
  def format(value, pos, round?) when is_float(value),
    do: format(Float
               |> apply((if round?, do: :round, else: :floor), [value, pos]) 
               |> Float.to_string, pos)
  def format(value, 0, _) when is_binary(value),
    do: with [i | _] <- String.split(value, "."), do: i
  def format(value, pos, round?) when is_binary(value) do
    case String.split(value, ".") do
     [i] -> format(i <> ".0", pos, round?)
     [i, f] -> [i, f 
                   |> String.pad_trailing(pos, "0")
                   |> String.slice(0..pos - 1)] |> Enum.join(".")
    end
  end
end

IO.inspect NumFmt.format(123.101, 6), label: "123.101000"
#⇒123.101000: "123.101000"

IO.inspect NumFmt.format(123, 3), label: "123.000"
#⇒ 123.000: "123.000"

IO.inspect NumFmt.format(123.101, 1), label: "123.1"
#⇒ 123.1: "123.1"

IO.inspect NumFmt.format(123.000, 0), label: "123"
#⇒ 123: "123"

Upvotes: 2

Dogbert
Dogbert

Reputation: 222060

There isn't anything like this in Elixir's standard library as far as I know, but you can use :io_lib.format/2 from Erlang to print a float with specific number of digits after the dot:

iex(1)> :io_lib.format("~.3f", [123.101])
['123.101']
iex(2)> :io_lib.format("~.6f", [123.101])
['123.101000']
iex(3)> :io_lib.format("~.3f", [123.0])
['123.000']
iex(4)> :io_lib.format("~.6f", [123.0])
['123.000000']

The returned value is an iolist, which can be converted to a binary using IO.iodata_to_binary/1 if required (many functions that take a binary can accept an iolist directly as well, e.g. IO.puts/1):

iex(5)> :io_lib.format("~.6f", [123.0]) |> IO.iodata_to_binary
"123.000000"

~.0f doesn't work but for that you can simply call trunc/1:

iex(6)> trunc(123.123)
123

Upvotes: 7

Mike Buhot
Mike Buhot

Reputation: 4885

I've used the decimal package for this previously

iex(6)> 123 |> Decimal.new() |> Decimal.round(3) |> Decimal.to_string()
"123.000"

In my case the data was already in Decimal format after being queried from the database.

Upvotes: 2

Related Questions