122

I need to convert a string to a floating point value or an integer. There was no method such as,

string_to_integer
Ramon Snir
  • 7,520
  • 3
  • 43
  • 61
Lahiru
  • 2,609
  • 3
  • 18
  • 29

8 Answers8

190

Check Integer.parse/1 and Float.parse/1.

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
José Valim
  • 50,409
  • 12
  • 130
  • 115
  • 47
    Note that this will return a tuple (if successful) and not the integer directly. If you want to do that, see @Szymon Jeż answer with `String.to_integer/1` –  Sep 26 '16 at 12:27
  • 7
    Is there any reason to use `Integer.parse/1` over `String.to_integer/1`? – Ian Vaughan Apr 06 '17 at 08:06
  • 12
    @IanVaughan `Integer.parse/1` returns a `:error` atom if unsuccessful. `String.to_integer/1` throws a `(FunctionClauseError)`. – Jonathan Soifer Apr 14 '17 at 21:54
  • 1
    To add to Jonathan's point, `String.to_float("0")` throws an error, while `Float.parse("0")` returns `{0.0, ""}`. This allowed me to use a single function when my string might not include a decimal point. – acheroncaptain Dec 04 '20 at 23:48
58

In addition to the Integer.parse/1 and Float.parse/1 functions which José suggested you may also check String.to_integer/1 and String.to_float/1.

Hint: See also to_atom/1,to_char_list/1,to_existing_atom/1for other conversions.

Szymon Jeż
  • 8,273
  • 4
  • 42
  • 60
36

Thanks folks on this page, just simplifying an answer here:

{int_val, ""} = Integer.parse(val)

as it validates that the entire string was parsed (not just a prefix).

hayesgm
  • 8,678
  • 1
  • 38
  • 40
Roozbeh Zabihollahi
  • 7,207
  • 45
  • 39
  • This will throw an error if the val is not purely an integer. I added a case on the result to ensure the conversion was successful. The second clause can be generic to catch :error or a non-empty second string as you don't care much whether the input was "x3" or "3x". – Sinc Apr 14 '20 at 20:22
27

There are 4 functions to create number from string

  • String.to_integer, String.to_float
  • Integer.parse, Float.parse

String.to_integer works nicely but String.to_float is tougher:

iex()> "1 2 3 10 100" |> String.split |> Enum.map(&String.to_integer/1)
[1, 2, 3, 10, 100]

iex()> "1.0 1 3 10 100" |> String.split |> Enum.map(&String.to_float/1)
** (ArgumentError) argument error
    :erlang.binary_to_float("1")
    (elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2

As String.to_float can only handle well-formatted float, e.g: 1.0, not 1 (integer). That was documented in String.to_float's doc

Returns a float whose text representation is string.

string must be the string representation of a float including a decimal point. In order to parse a string without decimal point as a float then Float.parse/1 should be used. Otherwise, an ArgumentError will be raised.

But Float.parse returns a tuple of 2 elements, not the number you want, so put it into pipeline is not "cool":

iex()> "1.0 1 3 10 100" |> String.split \
|> Enum.map(fn n -> {v, _} = Float.parse(n); v end)

[1.0, 1.0, 3.0, 10.0, 100.0]

Using elem to get first element from tuple make it shorter and sweeter:

iex()> "1.0 1 3 10 100" |> String.split \
|> Enum.map(fn n -> Float.parse(n) |> elem(0) end)

[1.0, 1.0, 3.0, 10.0, 100.0]
Community
  • 1
  • 1
HVNSweeting
  • 2,859
  • 2
  • 35
  • 30
12

You can convert it to a char_list and then use the Erlang to_integer/1 or to_float/1.

E.g.

iex> {myInt, _} = :string.to_integer(to_char_list("23"))
{23, []}

iex> myInt
23
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • How to use it in functions? My best solution is `fn q -> {v, _} = Float.parse(q); v end` which I don't like. I like to use it in `Enum.map`, e.g. `list |> Enum.map(&String.to_float/1)` but string.to_float doesn't work for integer numbers? – Zhomart Jan 03 '17 at 06:32
5

The problem with using Integer.parse/1 is that is will parse any non-numeric part of the string as long as it is in the tail end. For example:

Integer.parse("01") # {1, ""}
Integer.parse("01.2") # {1, ".2"}
Integer.parse("0-1") # {0, "-1"}
Integer.parse("-01") # {-1, ""}
Integer.parse("x-01") # :error
Integer.parse("0-1x") # {0, "-1x"}

Similarly String.to_integer/1 has the following results:

String.to_integer("01") # 1
String.to_integer("01.2") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")
String.to_integer("0-1") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")
String.to_integer("-01") # -1
String.to_integer("x-01") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")
String.to_integer("0-1x") # ** (ArgumentError) argument error :erlang.binary_to_integer("01.2")

Instead, validate the string first.

re = Regex.compile!("^[+-]?[0-9]*\.?[0-9]*$")
Regex.match?(re, "01") # true
Regex.match?(re, "01.2") # true
Regex.match?(re, "0-1") # false
Regex.match?(re, "-01") # true
Regex.match?(re, "x-01") # false
Regex.match?(re, "0-1x") # false

The regular expression could be simpler (e.g. ^[0-9]*$) depending on your use case.

Nibir Bora
  • 81
  • 1
  • 1
5
Decimal.new("1") |> Decimal.to_integer
Decimal.new("1.0") |> Decimal.to_float
kangkyu
  • 5,252
  • 3
  • 34
  • 36
2

If you wanted to convert a string to whatever numeric type is within the string & remove all other characters, this is probably overkill, but will return a float if its a float or an int if its an int or nil if the string does not contain an numeric type.

@spec string_to_numeric(binary()) :: float() | number() | nil
def string_to_numeric(val) when is_binary(val), do: _string_to_numeric(Regex.replace(~r{[^\d\.]}, val, ""))
defp _string_to_numeric(val) when is_binary(val), do: _string_to_numeric(Integer.parse(val), val)
defp _string_to_numeric(:error, _val), do: nil
defp _string_to_numeric({num, ""}, _val), do: num
defp _string_to_numeric({num, ".0"}, _val), do: num
defp _string_to_numeric({_num, _str}, val), do: elem(Float.parse(val), 0)
jrichocean
  • 31
  • 4