Skip to content

Return infinities for overflowing real arithmetic - #31

Merged
polvalente merged 1 commit into
elixir-nx:mainfrom
blasphemetheus:fix/real-binary-op-overflow
Oct 8, 2026
Merged

polvalente merged 1 commit into
elixir-nx:mainfrom
blasphemetheus:fix/real-binary-op-overflow

Conversation

@blasphemetheus

Copy link
Copy Markdown
Contributor

Follow-up to the IEEE 754 work in ab2149e, which covered the unary and domain-error cases. Binary arithmetic on real numbers still crashes when the exact result overflows a float:

iex> max = 1.7976931348623157e308
iex> Complex.add(max, max)
** (ArithmeticError) bad argument in arithmetic expression
iex> Complex.multiply(max, 2.0)
** (ArithmeticError) bad argument in arithmetic expression
iex> Complex.pow(max, 2)
:nan

IEEE 754 wants :infinity, :infinity and :infinity. pow already rescued the error but answered :nan for everything except zero to a negative power, so an overflow looked like a domain error.

This rescues the overflow in the real-number clauses of add, subtract, multiply, divide and pow and picks the sign from the operands. pow keeps :nan for a negative base with a non-integer exponent and returns :neg_infinity for a negative base with an odd exponent. The %Complex{} clauses compute component-wise through these same functions, so Complex.add(Complex.new(max, 1.0), Complex.new(max, 1.0)) now gives Complex.new(:infinity, 2.0).

Downstream, Nx's BinaryBackend routes its element-wise arithmetic through these functions. With this change Nx.add, Nx.multiply, Nx.divide and Nx.pow on overflowing f64 inputs return infinities instead of raising, and Nx.product over 600 f16 values of 1e3 returns infinity instead of raising from its accumulator. All five were verified against the Nx test build with this branch swapped in as the dependency.

The new tests fail on main and pass here. The existing suite passes.

🤖 Generated with Claude Code

BEAM floats raise ArithmeticError when a result overflows, so
Complex.add, subtract, multiply, divide and pow on real numbers crashed
where IEEE 754 calls for an infinity. pow already rescued the error but
answered :nan for every case except 0 to a negative power, which hid
overflow behind a domain-error answer.

Rescue the overflow in the five real-number clauses and pick the sign
from the operands. pow keeps :nan for a negative base with a
non-integer exponent and returns :neg_infinity for a negative base with
an odd exponent. The %Complex{} clauses compute component-wise through
these functions, so complex values pick up the same behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread lib/complex.ex
Comment thread lib/complex.ex
@blasphemetheus
blasphemetheus marked this pull request as ready for review October 7, 2026 06:55
Comment thread lib/complex.ex
Comment on lines -409 to +413
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a contribution to :complex yet again

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I changed tabs too much. This is :complex hahhaha

Comment thread lib/complex.ex
Comment on lines -409 to +413
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I changed tabs too much. This is :complex hahhaha

@polvalente
polvalente merged commit e7d1612 into elixir-nx:main Oct 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants