@@ -1360,6 +1360,12 @@ defmodule Decimal do
13601360 iex> Decimal.new("3.14")
13611361 Decimal.new("3.14")
13621362
1363+ iex> Decimal.new("1.79769313486231581e308")
1364+ Decimal.new("1.79769313486231581e308")
1365+
1366+ iex> Decimal.new("2.22507385850720139e-308")
1367+ Decimal.new("2.22507385850720139e-308")
1368+
13631369 """
13641370 @ spec new ( decimal ) :: t
13651371 def new ( % Decimal { sign: sign , coef: coef , exp: exp } = num )
@@ -1664,13 +1670,28 @@ defmodule Decimal do
16641670
16651671 iex> Decimal.to_float(Decimal.new("1.5"))
16661672 1.5
1673+ iex> Decimal.to_float(Decimal.new("-1.79769313486231581e308"))
1674+ ** (Decimal.Error) : negative number smaller than DBL_MAX: Decimal.new("-1.79769313486231581E+308")
1675+
1676+
1677+ iex> Decimal.to_float(Decimal.new("-1.79769313486231581e308"))
1678+ ** (Decimal.Error) : negative number smaller than DBL_MAX: Decimal.new("-1.79769313486231581E+308")
1679+
1680+
1681+ iex> Decimal.to_float(Decimal.new("2.22507385850720139e-308"))
1682+ ** (Decimal.Error) : number smaller than DBL_MIN: Decimal.new("2.22507385850720139E-308")
1683+
1684+
1685+ iex> Decimal.to_float(Decimal.new("-2.22507385850720139e-308"))
1686+ ** (Decimal.Error): negative number bigger than DBL_MIN: Decimal.new(\" -2.22507385850720139E-308\" )
16671687
16681688 iex> Decimal.to_float(Decimal.new("inf"))
16691689 ** (ArgumentError) Infinity cannot be converted to float
16701690
16711691 """
16721692 @ spec to_float ( t ) :: float
1673- def to_float ( % Decimal { sign: sign , coef: coef , exp: exp } ) when is_integer ( coef ) do
1693+ def to_float ( % Decimal { coef: coef } = decimal ) when is_integer ( coef ) do
1694+ % Decimal { sign: sign , coef: coef , exp: exp } = check_dbl_min_max ( decimal )
16741695 # Convert back to float without loss
16751696 # http://www.exploringbinary.com/correct-decimal-to-floating-point-using-big-integers/
16761697 { num , den } = ratio ( coef , exp )
@@ -2059,15 +2080,15 @@ defmodule Decimal do
20592080 defp parse_float ( "." <> rest ) , do: parse_digits ( rest )
20602081 defp parse_float ( bin ) , do: { [ ] , bin }
20612082
2062- defp parse_exp ( << e , rest :: binary >> ) when e in [ ?e , ?E ] do
2063- case rest do
2064- << sign , rest :: binary >> when sign in [ ?+ , ?- ] ->
2065- { digits , rest } = parse_digits ( rest )
2066- { [ sign | digits ] , rest }
2083+ defp parse_exp ( << e , sign , digit , rest :: binary >> )
2084+ when e in [ ?e , ?E ] and sign in [ ?+ , ?- ] and digit in ?0 .. ?9 do
2085+ { digits , rest } = parse_digits ( rest )
2086+ { [ sign , digit | digits ] , rest }
2087+ end
20672088
2068- _ ->
2069- parse_digits ( rest )
2070- end
2089+ defp parse_exp ( << e , digit , rest :: binary >> ) when e in [ ?e , ?E ] and digit in ?0 .. ?9 do
2090+ { digits , rest } = parse_digits ( rest )
2091+ { [ digit | digits ] , rest }
20712092 end
20722093
20732094 defp parse_exp ( bin ) do
@@ -2125,6 +2146,38 @@ defmodule Decimal do
21252146
21262147 defp fix_float_exp ( [ ] , result ) , do: :lists . reverse ( result )
21272148
2149+ defp check_dbl_min_max ( % Decimal { coef: :inf } = infinity ) , do: infinity
2150+
2151+ defp check_dbl_min_max ( % Decimal { sign: 1 } = num ) do
2152+ cond do
2153+ Decimal . gt? ( num , dbl_max ( 1 ) ) ->
2154+ raise Error , reason: "number bigger than DBL_MAX: #{ inspect ( num ) } "
2155+
2156+ Decimal . gt? ( num , zero ( 1 ) ) and Decimal . lt? ( num , dbl_min ( 1 ) ) ->
2157+ raise Error , reason: "number smaller than DBL_MIN: #{ inspect ( num ) } "
2158+
2159+ true ->
2160+ num
2161+ end
2162+ end
2163+
2164+ defp check_dbl_min_max ( num ) do
2165+ cond do
2166+ Decimal . lt? ( num , dbl_max ( - 1 ) ) ->
2167+ raise Error , reason: "negative number smaller than DBL_MAX: #{ inspect ( num ) } "
2168+
2169+ Decimal . lt? ( num , zero ( - 1 ) ) and Decimal . gt? ( num , dbl_min ( - 1 ) ) ->
2170+ raise Error , reason: "negative number bigger than DBL_MIN: #{ inspect ( num ) } "
2171+
2172+ true ->
2173+ num
2174+ end
2175+ end
2176+
2177+ defp dbl_min ( sign ) , do: % Decimal { sign: sign , coef: 22_250_738_585_072_014 , exp: - 324 }
2178+ defp zero ( sign ) , do: % Decimal { sign: sign , coef: 0 , exp: 0 }
2179+ defp dbl_max ( sign ) , do: % Decimal { sign: sign , coef: 17_976_931_348_623_158 , exp: 292 }
2180+
21282181 if Version . compare ( System . version ( ) , "1.3.0" ) == :lt do
21292182 defp integer_to_charlist ( string ) , do: Integer . to_char_list ( string )
21302183 else
0 commit comments