0

I'm getting this error trying to parse a string to a float:

case insert_product_shop(conn, existing_product.id, existing_shop.id, String.to_float(posted_product["price"])) do

Error:

20:45:29.766 [error] #PID<0.342.0> running Api.Router terminated
Server: 172.20.10.2:4000 (http)
Request: POST /products
** (exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.binary_to_float(58.25)
        (api) lib/api/router.ex:120: anonymous fn/1 in Api.Router.do_match/4
        (api) lib/api/router.ex:1: Api.Router.plug_builder_call/2
        (api) lib/plug/debugger.ex:123: Api.Router.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) /Users/Ben/Development/Projects/vepo/api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.exe
cute/4

When I change to

case insert_product_shop(conn, existing_product.id, existing_shop.id, Float.parse(posted_product["price"])) do
:

I get:

20:54:12.769 [error] #PID<0.336.0> running Api.Router terminated
Server: 172.20.10.2:4000 (http)
Request: POST /products
** (exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.binary_to_float(24)
        (api) lib/api/router.ex:82: anonymous fn/1 in Api.Router.do_match/4
        (api) lib/api/router.ex:1: Api.Router.plug_builder_call/2
        (api) lib/plug/debugger.ex:123: Api.Router.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) /Users/Ben/Development/Projects/vepo/api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.exe
cute/4

How can I properly parse from string to float? Can I also allow it to be either a string or float and if it is string, parse to float?

1
  • 3
    You already have a float according to the error message. Just use posted_product["price"]. Commented Aug 10, 2017 at 9:37

1 Answer 1

4

According to the error, it looks like the input is already a Float.

** (ArgumentError) argument error
    :erlang.binary_to_float(58.25)

In fact, String.to_float/1 raises an error if the input is not a String.

iex(1)> String.to_float(58.25)
** (ArgumentError) argument error
    :erlang.binary_to_float(58.25)
iex(1)> String.to_float("58.25")
58.25

Also note that String.to_float/1 also complains if the input has no decimal digits.

iex(2)> String.to_float("58")
** (ArgumentError) argument error
    :erlang.binary_to_float("58")
iex(2)> String.to_float("58.0")
58.0

You need to write a custom function. I'm not sure where posted_product["price"] is coming from, and whether effectively you expect it to have different input types. That may be a bug.

One possible workaround is to always cast the input to String, and use Float.parse/1

iex(12)> {f, _} = Float.parse(to_string("58.0"))
{58.0, ""}
iex(13)> f
58.0
iex(14)> {f, _} = Float.parse(to_string(58.0))
{58.0, ""}
iex(15)> f
58.0

Note that Float.parse/1 may return an :error, hence you must handle that.

Another option, perhaps slightly more efficient, is to use is_float/1 and is_string/1 to handle the conversion only if necessary.

defmodule Price do
  def to_float(input) when is_float(input) do
    input
  end
  def to_float(input) do
    case Float.parse(to_string(input)) do
      {f, ""} -> f
      _       -> :error
    end
  end
end

iex(2)> Price.to_float(32)
32.0
iex(3)> Price.to_float(32.0)
32.0
iex(4)> Price.to_float("32")
32.0
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.