Reputation: 12483
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?
Upvotes: 0
Views: 960
Reputation: 176522
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
Upvotes: 4