Skip to content

Commit 11c84cb

Browse files
author
José Valim
committed
Delegate to :erlang.binary_to_float on Float.parse/1
Signed-off-by: José Valim <[email protected]>
1 parent 67501bc commit 11c84cb

File tree

1 file changed

+18
-54
lines changed

1 file changed

+18
-54
lines changed

lib/elixir/lib/float.ex

Lines changed: 18 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,75 +28,39 @@ defmodule Float do
2828
"""
2929
@spec parse(binary) :: {float, binary} | :error
3030
def parse("-" <> binary) do
31-
case parse_unsign(binary) do
31+
case parse_unsigned(binary) do
3232
:error -> :error
3333
{number, remainder} -> {-number, remainder}
3434
end
3535
end
3636

3737
def parse(binary) do
38-
parse_unsign(binary)
38+
parse_unsigned(binary)
3939
end
4040

41-
defp parse_unsign("-" <> _), do: :error
42-
defp parse_unsign(binary) when is_binary(binary) do
43-
case Integer.parse binary do
44-
:error -> :error
45-
{integer_part, after_integer} -> parse_unsign after_integer, integer_part
46-
end
47-
end
41+
defp parse_unsigned(<<char, rest::binary>>) when char in ?0..?9, do:
42+
parse_unsigned(rest, false, false, <<char>>)
4843

49-
# Dot followed by digit is required afterwards or we are done
50-
defp parse_unsign(<< ?., char, rest :: binary >>, int) when char in ?0..?9 do
51-
parse_unsign(rest, char - ?0, 1, int)
52-
end
44+
defp parse_unsigned(binary) when is_binary(binary), do:
45+
:error
5346

54-
defp parse_unsign(rest, int) do
55-
{:erlang.float(int), rest}
56-
end
47+
defp parse_unsigned(<<char, rest :: binary>>, dot?, e?, acc) when char in ?0..?9, do:
48+
parse_unsigned(rest, dot?, e?, <<acc::binary, char>>)
5749

58-
# Handle decimal points
59-
defp parse_unsign(<< char, rest :: binary >>, float, decimal, int) when char in ?0..?9 do
60-
parse_unsign rest, 10 * float + (char - ?0), decimal + 1, int
61-
end
50+
defp parse_unsigned(<<?., char, rest :: binary>>, false, false, acc) when char in ?0..?9, do:
51+
parse_unsigned(rest, true, false, <<acc::binary, ?., char>>)
6252

63-
defp parse_unsign(<< ?e, after_e :: binary >>, float, decimal, int) do
64-
case Integer.parse after_e do
65-
:error ->
66-
# Note we rebuild the binary here instead of breaking it apart at
67-
# the function clause because the current approach copies a binary
68-
# just on this branch. If we broke it apart in the function clause,
69-
# the copy would happen when calling Integer.parse/1.
70-
{floatify(int, float, decimal), << ?e, after_e :: binary >>}
71-
{exponential, after_exponential} ->
72-
{floatify(int, float, decimal, exponential), after_exponential}
73-
end
74-
end
75-
76-
defp parse_unsign(bitstring, float, decimal, int) do
77-
{floatify(int, float, decimal), bitstring}
78-
end
53+
defp parse_unsigned(<<?e, char, rest :: binary>>, dot?, false, acc) when char in ?0..?9, do:
54+
parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, char>>)
7955

80-
defp floatify(int, float, decimal, exponential \\ 0) do
81-
multiplier = if int < 0, do: -1.0, else: 1.0
56+
defp parse_unsigned(<<?e, ?-, char, rest :: binary>>, dot?, false, acc) when char in ?0..?9, do:
57+
parse_unsigned(rest, true, true, <<add_dot(acc, dot?)::binary, ?e, ?-, char>>)
8258

83-
# Try to ensure the minimum amount of rounding errors
84-
result = multiplier * (abs(int) * :math.pow(10, decimal) + float) * :math.pow(10, exponential - decimal)
59+
defp parse_unsigned(rest, dot?, _e?, acc), do:
60+
{:erlang.binary_to_float(add_dot(acc, dot?)), rest}
8561

86-
# Try avoiding stuff like this:
87-
# iex(1)> 0.0001 * 75
88-
# 0.007500000000000001
89-
# Due to IEEE 754 floating point standard
90-
# http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
91-
92-
final_decimal_places = decimal - exponential
93-
if final_decimal_places > 0 do
94-
decimal_power_round = :math.pow(10, final_decimal_places)
95-
trunc(result * decimal_power_round) / decimal_power_round
96-
else
97-
result
98-
end
99-
end
62+
defp add_dot(acc, true), do: acc
63+
defp add_dot(acc, false), do: acc <> ".0"
10064

10165
@doc """
10266
Rounds a float to the largest integer less than or equal to `num`.

0 commit comments

Comments
 (0)