Skip to content

Commit 8f12969

Browse files
whatyouhidejosevalim
authored andcommitted
Don't raise on overflowing floats in Float.parse/1 (#12099)
When floats are expressed in scientific notation, :erlang.binary_to_float/1 can raise an ArgumentError if the e exponent is too big. For example, "1.0e400". Because of this, we rescue the ArgumentError and return an error.
1 parent ba03b8e commit 8f12969

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

lib/elixir/lib/float.ex

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ defmodule Float do
119119
returned.
120120
121121
If the size of float exceeds the maximum size of `1.7976931348623157e+308`,
122-
the `ArgumentError` exception is raised.
122+
`:error` is returned even though the textual representation itself might be
123+
well formed.
123124
124125
If you want to convert a string-formatted float directly to a float,
125126
`String.to_float/1` can be used instead.
@@ -135,6 +136,8 @@ defmodule Float do
135136
136137
iex> Float.parse("pi")
137138
:error
139+
iex> Float.parse("1.7976931348623159e+308")
140+
:error
138141
139142
"""
140143
@spec parse(binary) :: {float, binary} | :error
@@ -172,7 +175,18 @@ defmodule Float do
172175
when exp_marker in 'eE' and sign in '-+' and digit in ?0..?9,
173176
do: parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, sign, digit>>)
174177

175-
defp parse_unsigned(rest, dot?, _e?, acc),
178+
# When floats are expressed in scientific notation, :erlang.binary_to_float/1 can raise an
179+
# ArgumentError if the e exponent is too big. For example, "1.0e400". Because of this, we
180+
# rescue the ArgumentError here and return an error.
181+
defp parse_unsigned(rest, dot?, true = _e?, acc) do
182+
:erlang.binary_to_float(add_dot(acc, dot?))
183+
rescue
184+
ArgumentError -> :error
185+
else
186+
float -> {float, rest}
187+
end
188+
189+
defp parse_unsigned(rest, dot?, false = _e?, acc),
176190
do: {:erlang.binary_to_float(add_dot(acc, dot?)), rest}
177191

178192
defp add_dot(acc, true), do: acc

lib/elixir/test/elixir/float_test.exs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ defmodule FloatTest do
2424
assert Float.parse("-12.32453e-10") === {-1.232453e-9, ""}
2525
assert Float.parse("0.32453e-10") === {3.2453e-11, ""}
2626
assert Float.parse("1.32453e-10") === {1.32453e-10, ""}
27+
assert Float.parse("1.7976931348623159e-99999foo") === {0.0, "foo"}
2728
assert Float.parse("1.32.45") === {1.32, ".45"}
2829
assert Float.parse("1.o") === {1.0, ".o"}
2930
assert Float.parse("+12.3E+4") === {1.23e5, ""}
@@ -35,10 +36,12 @@ defmodule FloatTest do
3536
assert Float.parse("++1.2") === :error
3637
assert Float.parse("pi") === :error
3738
assert Float.parse("1.7976931348623157e308") === {1.7976931348623157e308, ""}
38-
39-
assert_raise ArgumentError, fn ->
40-
Float.parse("1.7976931348623159e308")
41-
end
39+
assert Float.parse("1.7976931348623157e308foo") === {1.7976931348623157e308, "foo"}
40+
assert Float.parse("1.7976931348623157e+308foo") === {1.7976931348623157e308, "foo"}
41+
assert Float.parse("1.7976931348623157e-308foo") === {1.7976931348623155e-308, "foo"}
42+
assert Float.parse("1.7976931348623159e308") === :error
43+
assert Float.parse("1.7976931348623159e+308") === :error
44+
assert Float.parse("9e8363") === :error
4245
end
4346

4447
test "floor/1" do

0 commit comments

Comments
 (0)