@@ -28,75 +28,39 @@ defmodule Float do
28
28
"""
29
29
@ spec parse ( binary ) :: { float , binary } | :error
30
30
def parse ( "-" <> binary ) do
31
- case parse_unsign ( binary ) do
31
+ case parse_unsigned ( binary ) do
32
32
:error -> :error
33
33
{ number , remainder } -> { - number , remainder }
34
34
end
35
35
end
36
36
37
37
def parse ( binary ) do
38
- parse_unsign ( binary )
38
+ parse_unsigned ( binary )
39
39
end
40
40
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 >> )
48
43
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
53
46
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 >> )
57
49
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 >> )
62
52
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 >> )
79
55
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 >> )
82
58
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 }
85
61
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"
100
64
101
65
@ doc """
102
66
Rounds a float to the largest integer less than or equal to `num`.
0 commit comments