@@ -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