diff --git a/lib/complex.ex b/lib/complex.ex index 5e0a2f7..6954fe8 100644 --- a/lib/complex.ex +++ b/lib/complex.ex @@ -406,7 +406,11 @@ defmodule Complex do def add(:neg_infinity, _), do: :neg_infinity def add(_, :neg_infinity), do: :neg_infinity - def add(left, right) when is_number(left) and is_number(right), do: left + right + def add(left, right) when is_number(left) and is_number(right) do + left + right + rescue + ArithmeticError -> if left > 0, do: :infinity, else: :neg_infinity + end def add(left, right) do %Complex{re: re_left, im: im_left} = as_complex(left) @@ -453,7 +457,11 @@ defmodule Complex do def subtract(:neg_infinity, _), do: :neg_infinity def subtract(_, :neg_infinity), do: :infinity - def subtract(left, right) when is_number(left) and is_number(right), do: left - right + def subtract(left, right) when is_number(left) and is_number(right) do + left - right + rescue + ArithmeticError -> if left > 0, do: :infinity, else: :neg_infinity + end def subtract(left, right) do %Complex{re: re_left, im: im_left} = as_complex(left) @@ -522,7 +530,11 @@ defmodule Complex do def multiply(:infinity, :neg_infinity), do: :neg_infinity def multiply(:infinity, :infinity), do: :infinity - def multiply(left, right) when is_number(left) and is_number(right), do: left * right + def multiply(left, right) when is_number(left) and is_number(right) do + left * right + rescue + ArithmeticError -> if left > 0 == right > 0, do: :infinity, else: :neg_infinity + end def multiply(left, right) do %Complex{re: r1, im: i1} = as_complex(left) @@ -585,7 +597,12 @@ defmodule Complex do def divide(x, 0) when is_number(x) and x > 0, do: :infinity def divide(x, 0) when is_number(x) and x < 0, do: :neg_infinity def divide(x, 0) when is_number(x), do: :nan - def divide(x, y) when is_number(x) and is_number(y), do: x / y + + def divide(x, y) when is_number(x) and is_number(y) do + x / y + rescue + ArithmeticError -> if x > 0 == y > 0, do: :infinity, else: :neg_infinity + end def divide(n, %Complex{re: re_r, im: im_r}) when is_number(n) and re_r in [:infinity, :neg_infinity] and im_r == 0 do @@ -1145,7 +1162,9 @@ defmodule Complex do ArithmeticError -> cond do x == 0 and y < 0 -> :infinity - true -> :nan + x < 0 and y != trunc(y) -> :nan + x < 0 and rem(trunc(y), 2) != 0 -> :neg_infinity + true -> :infinity end end diff --git a/test/complex_test.exs b/test/complex_test.exs index 43661b1..2130fd6 100644 --- a/test/complex_test.exs +++ b/test/complex_test.exs @@ -1042,6 +1042,52 @@ defmodule ComplexTest do end end + describe "IEEE 754: binary operation overflow returns Inf instead of crashing" do + @max_float 1.7976931348623157e308 + + test "add overflow" do + assert Complex.add(@max_float, @max_float) == :infinity + assert Complex.add(-@max_float, -@max_float) == :neg_infinity + end + + test "subtract overflow" do + assert Complex.subtract(@max_float, -@max_float) == :infinity + assert Complex.subtract(-@max_float, @max_float) == :neg_infinity + end + + test "multiply overflow" do + assert Complex.multiply(@max_float, 2.0) == :infinity + assert Complex.multiply(@max_float, -2.0) == :neg_infinity + assert Complex.multiply(-@max_float, -2.0) == :infinity + assert Complex.multiply(@max_float, @max_float) == :infinity + end + + test "divide overflow" do + assert Complex.divide(@max_float, 0.5) == :infinity + assert Complex.divide(@max_float, -0.5) == :neg_infinity + assert Complex.divide(-@max_float, 0.5) == :neg_infinity + end + + test "pow overflow" do + assert Complex.pow(@max_float, 2) == :infinity + assert Complex.pow(@max_float, 2.0) == :infinity + assert Complex.pow(-@max_float, 3) == :neg_infinity + assert Complex.pow(-@max_float, 2) == :infinity + end + + test "pow domain errors still return :nan" do + assert Complex.pow(-8.0, 0.5) == :nan + end + + test "complex components overflow" do + assert Complex.add(Complex.new(@max_float, 1.0), Complex.new(@max_float, 1.0)) == + Complex.new(:infinity, 2.0) + + assert Complex.multiply(Complex.new(@max_float, 0.0), Complex.new(2.0, 0.0)) == + Complex.new(:infinity, 0.0) + end + end + describe "IEEE 754: domain errors return NaN instead of crashing" do test "asin outside [-1, 1]" do assert Complex.asin(2.0) == :nan