Skip to content

Commit 4500ed4

Browse files
authored
Merge branch 'main' into wm-document-encoder
2 parents 1597252 + 8843f5b commit 4500ed4

File tree

3 files changed

+82
-9
lines changed

3 files changed

+82
-9
lines changed

lib/decimal.ex

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

test/decimal_test.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ defmodule DecimalTest do
7979
assert Decimal.parse("a") == :error
8080
assert Decimal.parse("test") == :error
8181
assert Decimal.parse("e0") == :error
82+
83+
assert Decimal.parse("1e-d") == {d(1, 1, 0), "e-d"}
8284
end
8385

8486
test "nan?/1" do

test/test_helper.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,22 @@ defmodule TestMacros do
1212
Decimal.new(unquote(str))
1313
end
1414
end
15+
16+
defmacro dbl_min(sign) do
17+
quote do
18+
%Decimal{sign: unquote(sign), coef: 22_250_738_585_072_014, exp: -324}
19+
end
20+
end
21+
22+
defmacro zero(sign) do
23+
quote do
24+
%Decimal{sign: unquote(sign), coef: 0, exp: 0}
25+
end
26+
end
27+
28+
defmacro dbl_max(sign) do
29+
quote do
30+
%Decimal{sign: unquote(sign), coef: 17_976_931_348_623_158, exp: 292}
31+
end
32+
end
1533
end

0 commit comments

Comments
 (0)