Skip to content

Commit

Permalink
feat: check dbl_min_max on Decimal.new method
Browse files Browse the repository at this point in the history
  • Loading branch information
karlosmid committed May 2, 2024
1 parent d110b7d commit aa3dae5
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 10 deletions.
62 changes: 54 additions & 8 deletions lib/decimal.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1257,20 +1257,32 @@ defmodule Decimal do
iex> Decimal.new("3.14")
Decimal.new("3.14")
iex> Decimal.new("1.79769313486231581e308")
** (Decimal.Error) : number bigger than DBL_MAX: Decimal.new("1.79769313486231581E+308")
iex> Decimal.new("2.22507385850720139e-308")
** (Decimal.Error) : number smaller than DBL_MIN: Decimal.new("2.22507385850720139E-308")
"""
@spec new(decimal) :: t
def new(%Decimal{sign: sign, coef: coef, exp: exp} = num)
when sign in [1, -1] and ((is_integer(coef) and coef >= 0) or coef in [:NaN, :inf]) and
is_integer(exp),
do: num
is_integer(exp) do
check_dbl_min_max(num)
end

def new(int) when is_integer(int),
do: %Decimal{sign: if(int < 0, do: -1, else: 1), coef: Kernel.abs(int)}
def new(int) when is_integer(int) do
num = %Decimal{sign: if(int < 0, do: -1, else: 1), coef: Kernel.abs(int)}
check_dbl_min_max(num)
end

def new(binary) when is_binary(binary) do
case parse(binary) do
{decimal, ""} -> decimal
_ -> raise Error, reason: "number parsing syntax: #{inspect(binary)}"
{decimal, ""} ->
check_dbl_min_max(decimal)

_ ->
raise Error, reason: "number parsing syntax: #{inspect(binary)}"
end
end

Expand All @@ -1290,8 +1302,10 @@ defmodule Decimal do
@spec new(sign :: 1 | -1, coef :: non_neg_integer | :NaN | :inf, exp :: integer) :: t
def new(sign, coef, exp)
when sign in [1, -1] and ((is_integer(coef) and coef >= 0) or coef in [:NaN, :inf]) and
is_integer(exp),
do: %Decimal{sign: sign, coef: coef, exp: exp}
is_integer(exp) do
num = %Decimal{sign: sign, coef: coef, exp: exp}
check_dbl_min_max(num)
end

@doc """
Creates a new decimal number from a floating point number.
Expand Down Expand Up @@ -2014,6 +2028,38 @@ defmodule Decimal do

defp fix_float_exp([], result), do: :lists.reverse(result)

defp check_dbl_min_max(%Decimal{coef: :inf} = infinity), do: infinity

defp check_dbl_min_max(%Decimal{sign: 1} = num) do
cond do
Decimal.gt?(num, dbl_max(1)) ->
raise Error, reason: "number bigger than DBL_MAX: #{inspect(num)}"

Decimal.gt?(num, zero(1)) and Decimal.lt?(num, dbl_min(1)) ->
raise Error, reason: "number smaller than DBL_MIN: #{inspect(num)}"

true ->
num
end
end

defp check_dbl_min_max(num) do
cond do
Decimal.lt?(num, dbl_max(-1)) ->
raise Error, reason: "negative number smaller than DBL_MAX: #{inspect(num)}"

Decimal.lt?(num, zero(-1)) and Decimal.gt?(num, dbl_min(-1)) ->
raise Error, reason: "negative number bigger than DBL_MIN: #{inspect(num)}"

true ->
num
end
end

def dbl_min(sign), do: %Decimal{sign: sign, coef: 22_250_738_585_072_014, exp: -324}
def zero(sign), do: %Decimal{sign: sign, coef: 0, exp: 0}
def dbl_max(sign), do: %Decimal{sign: sign, coef: 17_976_931_348_623_158, exp: 292}

if Version.compare(System.version(), "1.3.0") == :lt do
defp integer_to_charlist(string), do: Integer.to_char_list(string)
else
Expand Down
2 changes: 1 addition & 1 deletion lib/decimal/context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ defmodule Decimal.Context do
Runs function with given context.
"""
doc_since("1.9.0")
@spec with(t(), (() -> x)) :: x when x: var
@spec with(t(), (-> x)) :: x when x: var
def with(%Context{} = context, fun) when is_function(fun, 0) do
old = Process.put(@context_key, context)

Expand Down
42 changes: 41 additions & 1 deletion test/decimal_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,9 @@ defmodule DecimalTest do

assert Decimal.compare("Inf", "Inf") == :eq

assert Decimal.compare(~d"5e10000000000", ~d"0") == :gt
assert_raise Decimal.Error,
": number bigger than DBL_MAX: Decimal.new(\"5E+10000000000\")",
fn -> Decimal.compare(~d"5e10000000000", ~d"0") end

assert_raise Error, fn ->
Decimal.compare(~d"nan", ~d"0")
Expand Down Expand Up @@ -906,4 +908,42 @@ defmodule DecimalTest do
Decimal.sqrt(Decimal.new(d(3, 1, -1)))
end
end

test "max_min_dbl" do
assert Decimal.new("1.7976931348623158e308") == Decimal.dbl_max(1)
assert Decimal.new("-1.7976931348623158e308") == Decimal.dbl_max(-1)

assert_raise Decimal.Error,
": number bigger than DBL_MAX: Decimal.new(\"1.79769313486231581E+308\")",
fn -> Decimal.new("1.79769313486231581e308") end

assert_raise Decimal.Error,
": negative number smaller than DBL_MAX: Decimal.new(\"-1.79769313486231581E+308\")",
fn -> Decimal.new("-1.79769313486231581e308") end

assert Decimal.new("1.79769313486231579e308") == Decimal.new("1.79769313486231579E+308")
assert Decimal.new("-1.79769313486231579e308") == Decimal.new("-1.79769313486231579E+308")

assert Decimal.new("2.2250738585072014e-308") == Decimal.dbl_min(1)
assert Decimal.new("-2.2250738585072014e-308") == Decimal.dbl_min(-1)

assert_raise Decimal.Error,
": number smaller than DBL_MIN: Decimal.new(\"2.22507385850720139E-308\")",
fn -> Decimal.new("2.22507385850720139e-308") end

assert_raise Decimal.Error,
": negative number bigger than DBL_MIN: Decimal.new(\"-2.22507385850720139E-308\")",
fn -> Decimal.new("-2.22507385850720139e-308") end

assert Decimal.new("2.22507385850720141e-308") == Decimal.new("2.22507385850720141E-308")
assert Decimal.new("-2.22507385850720141e-308") == Decimal.new("-2.22507385850720141E-308")

assert_raise Decimal.Error,
": number bigger than DBL_MAX: Decimal.new(\"9.999999999999999999E+1000000000000000000000017\")",
fn -> Decimal.new("9999999999999999999e999999999999999999999999") end

assert_raise Decimal.Error,
": number smaller than DBL_MIN: Decimal.new(\"9.9999999999999E-999999999999999999999986\")",
fn -> Decimal.new("99999999999999e-999999999999999999999999") end
end
end

0 comments on commit aa3dae5

Please sign in to comment.