Skip to content

[Breaking change]: System.Numerics.Complex special-value results now follow C23 Annex G #54845

Description

@tannergooding

Description

System.Numerics.Complex arithmetic operators and math functions now produce
the special-value results (for signed zeros, infinities, and NaNs) required by
C23 Annex G (IEC 60559-compatible complex arithmetic). Because the shipped
Complex type now delegates most of its implementation to the new
Complex<double>, the conformant special-value handling flows through to it.

Many special-value inputs that previously returned (NaN, NaN) (or overflowed
to (∞, ∞)) now return the directed infinity, signed zero, or otherwise-specified
result that Annex G requires. Finite-input numeric results are unchanged.

Introduced by dotnet/runtime#131132.

Version

.NET 11 Preview 7

Previous behavior

For non-finite (and some overflowing) inputs, Complex arithmetic and elementary
functions frequently returned (NaN, NaN) even when a directed infinity or signed
result was mathematically appropriate.

using System.Numerics;

Complex.Atan(new Complex(double.PositiveInfinity, 1.0)); // (NaN, NaN)
Complex.Acos(new Complex(double.NegativeInfinity, double.NaN)); // (NaN, NaN)
Complex.Cosh(new Complex(double.PositiveInfinity, double.PositiveInfinity)); // (NaN, NaN)

// An infinite operand could collapse the product to NaN:
new Complex(double.PositiveInfinity, double.PositiveInfinity) * new Complex(1.0, 0.0); // (NaN, NaN)

New behavior

The same inputs now return the C23 Annex G special values.

using System.Numerics;

Complex.Atan(new Complex(double.PositiveInfinity, 1.0)); // (π/2, 0)
Complex.Acos(new Complex(double.NegativeInfinity, double.NaN)); // (NaN, +∞)
Complex.Cosh(new Complex(double.PositiveInfinity, double.PositiveInfinity)); // (+∞, NaN)

// An infinite operand now yields a directed infinity (Annex G.5.1 recovery):
new Complex(double.PositiveInfinity, double.PositiveInfinity) * new Complex(1.0, 0.0); // (+∞, +∞)

Division by a zero divisor is likewise governed by Annex G: the result is a
directed infinity, or NaN for 0/0 (for example, (1, 0) / (0, 0) yields an
infinite real component rather than a fully NaN result).

The change spans operator *, operator /, Multiply, Divide, Reciprocal,
Abs, Pow, and the elementary functions (Sqrt, Exp, Log, Log10, and the
trigonometric / hyperbolic / inverse-trigonometric functions). One case Annex G
leaves explicitly unspecified — the sign of a zero-valued quotient component from
operator / — may also differ.

Type of breaking change

  • Behavioral change: existing binaries behave differently at runtime.

Source and binary compatibility are unaffected — signatures are unchanged; only
the runtime results for special-value inputs differ.

Reason for change

Complex numbers are outside the scope of IEEE 754 itself, so C23 Annex G is the
relevant specification of the special-value behavior IEEE 754 otherwise implies
for the scalar operations complex arithmetic is built on. The previous
implementation returned NaN for many inputs where Annex G requires a directed
infinity or a signed result, which is both non-conformant and less useful for
downstream numerical code (for example, losing the sign/direction information that
an infinite intermediate carries). The new generic Complex<T> was made
conformant, and the shipped Complex inherits that conformance by delegation.

Recommended action

Most code benefits from the more accurate results and needs no change. Code that
explicitly depended on the previous (NaN, NaN) results for special-value inputs
(for example, tests that assert NaN for Complex operations on infinities, or
logic that treats any non-finite input as producing NaN) should be updated to
expect the Annex G values. There is no compatibility switch to restore the
previous behavior.

Feature area

Core .NET libraries

Affected APIs

  • System.Numerics.Complex.op_Multiply(System.Numerics.Complex, System.Numerics.Complex)
  • System.Numerics.Complex.op_Division(System.Numerics.Complex, System.Numerics.Complex)
  • System.Numerics.Complex.Multiply(System.Numerics.Complex, System.Numerics.Complex)
  • System.Numerics.Complex.Divide(System.Numerics.Complex, System.Numerics.Complex)
  • System.Numerics.Complex.Reciprocal(System.Numerics.Complex)
  • System.Numerics.Complex.Abs(System.Numerics.Complex)
  • System.Numerics.Complex.Pow(System.Numerics.Complex, System.Numerics.Complex) and Pow(System.Numerics.Complex, System.Double)
  • System.Numerics.Complex.Sqrt(System.Numerics.Complex)
  • System.Numerics.Complex.Exp(System.Numerics.Complex)
  • System.Numerics.Complex.Log(System.Numerics.Complex) (all overloads) and System.Numerics.Complex.Log10(System.Numerics.Complex)
  • System.Numerics.Complex.Sin, Cos, Tan, Sinh, Cosh, Tanh
  • System.Numerics.Complex.Asin, Acos, Atan

Note

This issue body was drafted by GitHub Copilot.


Associated WorkItem - 598918

Metadata

Metadata

Assignees

Labels

📌 seQUESTeredIdentifies that an issue has been imported into Quest.breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

Status
🔖 Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions