Skip to content

Complete NotANumber - #86

Merged
dlfivefifty merged 4 commits into
JuliaMath:masterfrom
PatrickHaecker:not_a_number
Sep 25, 2026
Merged

dlfivefifty merged 4 commits into
JuliaMath:masterfrom
PatrickHaecker:not_a_number

Conversation

@PatrickHaecker

@PatrickHaecker PatrickHaecker commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

NotANumber existed but could not really be used except as the value itself. Every
operation on it was a MethodError or an ErrorException, so an undefined result
could not be carried any further. Most of the places that should have produced a
NotANumber threw instead.

The current PR makes it behave like a float NaN instead.
It answers isnan, compares false against everything including itself, keeps isequal
and hash so a container can hold one, sorts last, and converts to the NaN of any
float type. Every arithmetic operation propagates it.

It also becomes a Real. Dividing two real numbers has to give a real number, and
+∞/+∞ is a NotANumber. Previously, it had isreal(+∞/+∞) false.
Real as supertype allows Complex{NotANumber}, so an operation with a complex
operand answers NotANumber() + NotANumber()*im, like the floats do.

Behaviour that changes:

  • ∞ + (-∞) and 0 * ∞ answer NotANumber() instead of throwing an ArgumentError.
  • NaN + ∞ answers NotANumber() instead of the float NaN. Every other float special
    value already loses its precision here — Inf32 + ∞ is ∞.
  • isreal(NotANumber()) is true.

mod is deliberately left alone. Base answers mod(-2.0, Inf) with -2.0, not NaN,
so NotANumber() would be the wrong answer there; mod(x, ∞) keeps throwing as before.

Based on #82, so it stays a draft until that is merged.

@PatrickHaecker PatrickHaecker changed the title Complete NotA Number Complete NotANumber Sep 4, 2026
@codecov

codecov Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aabb400) to head (a28f6ba).

Additional details and impacted files
@@            Coverage Diff            @@
##            master       #86   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            6         6           
  Lines          301       336   +35     
=========================================
+ Hits           301       336   +35     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

Test coverage is now 100%.

@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

Working at ComplexInfinity it becomes more and more clear that NotANumber should probably be Real to not clash with Base definitions in the complex case. I am currently trying whether that becomes more consistent if we change the current PR accordingly. So let's wait and see.

@PatrickHaecker
PatrickHaecker force-pushed the not_a_number branch 3 times, most recently from 8e5442b to ecf62b0 Compare September 6, 2026 06:21
@PatrickHaecker

Copy link
Copy Markdown
Contributor Author

Yes, this is more consistent. So changed accordingly.

Patrick Häcker added 4 commits September 23, 2026 11:52
It satisfies `isnan`, compares false against everything including itself, keeps
`isequal` and `hash` so a container can hold one, sorts last, and converts to
the `NaN` of any float type.

It also becomes a `Real`. Dividing two real numbers has to give a real
number, and `+∞/+∞` is a `NotANumber`, so as a `Number` it made
`isreal(+∞/+∞)` false. The supertype is also what lets `Complex{NotANumber}`
exist, since `Complex` takes a `Real`.

The price is dispatch. A `Real` matches Base's own methods for `Real`, which
are exactly as specific, so every such slot has to be filled here.
`NotANumberRivals` lists the types that need one.
Every operation on it was a MethodError or an ErrorException, so an undefined result could not be carried any further.
Adding infinities of opposite direction and multiplying an infinity by zero threw an ArgumentError, where the floats give NaN. The mod case is left alone, since Base returns -2.0 for mod(-2.0, Inf) rather than NaN.
The float NaN was returned unchanged, keeping its precision, where every other float special value loses it: Inf32 + ∞ is already ∞. This reverses a tested line of the NaN arithmetic PR.
@PatrickHaecker
PatrickHaecker marked this pull request as ready for review September 23, 2026 09:56
@PatrickHaecker PatrickHaecker mentioned this pull request Sep 23, 2026
Comment thread src/algebra.jl
@dlfivefifty
dlfivefifty merged commit 08ccb94 into JuliaMath:master Sep 25, 2026
13 checks passed
@PatrickHaecker
PatrickHaecker deleted the not_a_number branch September 25, 2026 08:52
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