Skip to content

Division with ∞ - #68

Closed
dlfivefifty wants to merge 6 commits into
masterfrom
division-with-∞

Hidden character warning

The head ref may contain hidden characters: "division-with-\u221e"
Closed

dlfivefifty wants to merge 6 commits into
masterfrom
division-with-∞

Conversation

@dlfivefifty

Copy link
Copy Markdown
Member

No description provided.

@codecov

codecov Bot commented Dec 21, 2025 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 98.69%. Comparing base (88706fc) to head (ae634a1).
⚠️ Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
src/Infinities.jl 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #68      +/-   ##
==========================================
- Coverage   99.08%   98.69%   -0.39%     
==========================================
  Files           6        6              
  Lines         218      230      +12     
==========================================
+ Hits          216      227      +11     
- Misses          2        3       +1     

☔ 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 PatrickHaecker left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am not yet proposing changes, because I still don't really understand the interaction with #82, but this should not be merged as is and parts of it will probably be obsolete due to #82 (sorry for not checking #68 before implementing #82).

Comment thread src/Infinities.jl
convert(::Type{ComplexInfinity}, ::Infinity) = ComplexInfinity()
convert(::Type{ComplexInfinity{T}}, x::RealInfinity) where T = ComplexInfinity{T}(x)
convert(::Type{ComplexInfinity}, x::RealInfinity) = ComplexInfinity(x)
float(x::ComplexInfinity) = exp(im*angle(x)) * Inf

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not correct:

julia> float(ComplexInfinity())
Inf + NaN*im

PatrickHaecker pushed a commit to PatrickHaecker/Infinities.jl that referenced this pull request Sep 4, 2026
Gaps that JuliaMath#68 covers and this branch did not, plus the types they missed.

`round(x, ::RoundingMode)` was a MethodError, and `round(x; digits)` fell
through to `Base` and returned `Inf` rather than the infinity, disagreeing with
the plain `round(x)` next to it. `isinteger` and the four rounding functions
also answered for `Infinity` and `RealInfinity` alone, so a `ComplexInfinity`
raised a MethodError where `Base` answers `false` and the value itself for the
matching `Complex`, and `ℵ₀` took a rounding mode but not the keywords.

`float(::ComplexInfinity)` was a MethodError, the real infinities having got
theirs from the `AbstractFloat` conversion. JuliaMath#68 proposes `exp(im*angle(x))*Inf`,
which is unsound: `0 * Inf` is a `NaN`, so `float(ComplexInfinity())` gives
`Inf + NaN*im`, and the imaginary and negative real axes come back as diagonals.
`cospi`/`sinpi` are exact at the half-integers, so building the parts from them
keeps the axes exact. Two saturating parts can express only eight rays, so an
angle off them lands on the nearest one, which the test pins.
PatrickHaecker pushed a commit to PatrickHaecker/Infinities.jl that referenced this pull request Sep 6, 2026
Gaps that JuliaMath#68 covers and this branch did not, plus the types they missed.

`round(x, ::RoundingMode)` was a MethodError, and `round(x; digits)` fell
through to `Base` and returned `Inf` rather than the infinity, disagreeing with
the plain `round(x)` next to it. `isinteger` and the four rounding functions
were also defined for `Infinity` and `RealInfinity` alone, so a `ComplexInfinity`
raised a MethodError where `Base` returns `false` and the value itself for the
matching `Complex`, and `ℵ₀` took a rounding mode but not the keywords.

`float(::ComplexInfinity)` was a MethodError, the real infinities having got
theirs from the `AbstractFloat` conversion. JuliaMath#68 proposes `exp(im*angle(x))*Inf`,
which is unsound: `0 * Inf` is a `NaN`, so `float(ComplexInfinity())` gives
`Inf + NaN*im`, and the imaginary and negative real axes come back as diagonals.
`cospi`/`sinpi` are exact at the half-integers, so building the parts from them
keeps the axes exact. Two saturating parts can express only eight rays, so an
angle off them lands on the nearest one, which the test pins.
PatrickHaecker pushed a commit to PatrickHaecker/Infinities.jl that referenced this pull request Sep 6, 2026
Gaps that JuliaMath#68 covers and this branch did not, plus the types they missed.

`round(x, ::RoundingMode)` was a MethodError, and `round(x; digits)` fell
through to `Base` and returned `Inf` rather than the infinity, disagreeing with
the plain `round(x)` next to it. `isinteger` and the four rounding functions
were also defined for `Infinity` and `RealInfinity` alone, so a `ComplexInfinity`
raised a MethodError where `Base` returns `false` and the value itself for the
matching `Complex`, and `ℵ₀` took a rounding mode but not the keywords.

`float(::ComplexInfinity)` was a MethodError, the real infinities having got
theirs from the `AbstractFloat` conversion. JuliaMath#68 proposes `exp(im*angle(x))*Inf`,
which is unsound: `0 * Inf` is a `NaN`, so `float(ComplexInfinity())` gives
`Inf + NaN*im`, and the imaginary and negative real axes come back as diagonals.
`cospi`/`sinpi` are exact at the half-integers, so building the parts from them
keeps the axes exact. Two saturating parts can express only eight rays, so an
angle off them lands on the nearest one, which the test pins.
@PatrickHaecker

Copy link
Copy Markdown
Contributor

Everything from here should now be taken over into the other PRs.

🤖 helped me in checking for completeness and collecting this information:

Once #81, #82, #86 and #87 land, everything here should be covered: all sixteen division
assertions in this PR already pass on that stack, conj and the angle wrap come
with #82 and #87, and float(::ComplexInfinity) is replaced by a cospi/sinpi
version because exp(im*angle(x))*Inf gives Inf + NaN*im on the axes.

One deliberate difference: round(+∞, RoundNearest) returns +∞ there rather than
Inf, to agree with round(+∞), floor, ceil and trunc beside it, and because
ℵ₀ and im*∞ have no float to round to.

So I would suggest closing this once the other PRs are merged.

PatrickHaecker pushed a commit to PatrickHaecker/Infinities.jl that referenced this pull request Sep 7, 2026
Gaps that JuliaMath#68 covers and this branch did not, plus the types they missed.

`round(x, ::RoundingMode)` was a MethodError, and `round(x; digits)` fell
through to `Base` and returned `Inf` rather than the infinity, disagreeing with
the plain `round(x)` next to it. `isinteger` and the four rounding functions
were also defined for `Infinity` and `RealInfinity` alone, so a `ComplexInfinity`
raised a MethodError where `Base` returns `false` and the value itself for the
matching `Complex`, and `ℵ₀` took a rounding mode but not the keywords.

`float(::ComplexInfinity)` was a MethodError, the real infinities having got
theirs from the `AbstractFloat` conversion. JuliaMath#68 proposes `exp(im*angle(x))*Inf`,
which is unsound: `0 * Inf` is a `NaN`, so `float(ComplexInfinity())` gives
`Inf + NaN*im`, and the imaginary and negative real axes come back as diagonals.
`cospi`/`sinpi` are exact at the half-integers, so building the parts from them
keeps the axes exact. Two saturating parts can express only eight rays, so an
angle off them lands on the nearest one, which the test pins.
PatrickHaecker pushed a commit to PatrickHaecker/Infinities.jl that referenced this pull request Sep 20, 2026
Gaps that JuliaMath#68 covers and this branch did not, plus the types they missed.

`round(x, ::RoundingMode)` was a MethodError, and `round(x; digits)` fell
through to `Base` and returned `Inf` rather than the infinity, disagreeing with
the plain `round(x)` next to it. `isinteger` and the four rounding functions
were also defined for `Infinity` and `RealInfinity` alone, so a `ComplexInfinity`
raised a MethodError where `Base` returns `false` and the value itself for the
matching `Complex`, and `ℵ₀` took a rounding mode but not the keywords.

`float(::ComplexInfinity)` was a MethodError, the real infinities having got
theirs from the `AbstractFloat` conversion. JuliaMath#68 proposes `exp(im*angle(x))*Inf`,
which is unsound: `0 * Inf` is a `NaN`, so `float(ComplexInfinity())` gives
`Inf + NaN*im`, and the imaginary and negative real axes come back as diagonals.
`cospi`/`sinpi` are exact at the half-integers, so building the parts from them
keeps the axes exact. Two saturating parts can express only eight rays, so an
angle off them lands on the nearest one, which the test pins.
dlfivefifty pushed a commit that referenced this pull request Sep 23, 2026
* Give `ComplexInfinity` its `abs`, `sign`, `conj` and negation

All four were `MethodError`s for an angle that is not a multiple of π: negation existed
only for an integer factor, and the other three not at all.

Negation and conjugation rotate and reflect the angle, reduced so that both stay
involutions; `abs` is `∞` whichever way the infinity points; `sign` is the unit vector,
`cispi` giving it exactly on the axes. The integer factor keeps its own methods, which
already returned an `Int` and are what `AllRealInfinities` relies on.

* Give an infinite complex summand the direction it points in

`ComplexInfinity` stores its direction in half turns, but `toinf` filled the
field with the radians of `angle(x)`. The direction of an infinite complex
summand was therefore off by a factor of π: `angle(toinf(complex(0, Inf)))`
gave 4.93 rather than `π/2`.

`_infadd` compares those angles, so a sum whose parts point the same way threw
although `==` called them equal: both `complex(-Inf, 0.0) + -∞` and
`complex(0.0, Inf) + im*∞` raised an ArgumentError. Only the positive real
axis escaped, angle `0` being the fixed point of the missing scaling.

`_sb` is the conversion the multiplication already uses, so it moves above the
addition and both sections share it.

* Add `isinteger` and the rounding functions for an infinity

Both were `MethodError`s, which also made `∞ in 1:5` fail, a range asking `isinteger`
before it compares. `Inf` is not an integer and rounding leaves it alone, so an infinity
does the same and returns itself.

`InfiniteCardinal` is left out of both: it is an `Integer`, for which `Base` already
returns `true` and the value unchanged.

* Divide by and into an infinity

`∞ / 2` and `2 / ∞` were promotion errors, though `inv` was already there to build them
from: division is multiplication by the inverse, which brings the sign and the `NaN`
handling of `*` with it. `\` needs nothing of its own, `Base` defining it as `y / x`.

`∞ / ∞` returns `NotANumber`, as `div(∞, ∞)` and `mod(∞, ∞)` already do, rather than the
`NaN` of the floats. `2 / ∞` inherits the `Int` zero of `inv(∞)` where the floats give
`0.0`, which fixing `inv` will settle in one place. `Rational` and `Complex` need the same
explicit pairs in `ambiguities.jl` as the other operators.

* Take the remainder against an infinity

`3 % ∞` was a promotion error, though `mod` and `div` were both already there. `rem` keeps
the sign of the dividend, so unlike `mod` it needs no bound: `-3 % ∞` is `-3`, where
`mod(-3, ∞)` is unbounded and throws. `divrem` follows from the two.

The other direction returns `NotANumber`, as `mod(∞, x)` and `div(∞, ∞)` do. `Rational`
and `BigInt` need the explicit pairs in `ambiguities.jl`, an `InfiniteCardinal` being an
`Integer` that `Base` has its own methods for.

* Let `isapprox` compare an infinity

`∞ ≈ Inf` threw, `Base` promoting its arguments before it compares them and an infinity
having no common type with a number. Nothing is near an infinity but an equal one, which
is what the floats do too, so approximate equality is exact equality and the keywords
have nothing to loosen.

* Complete `float` and the rounding forms for an infinity

Gaps that #68 covers and this branch did not, plus the types they missed.

`round(x, ::RoundingMode)` was a MethodError, and `round(x; digits)` fell
through to `Base` and returned `Inf` rather than the infinity, disagreeing with
the plain `round(x)` next to it. `isinteger` and the four rounding functions
were also defined for `Infinity` and `RealInfinity` alone, so a `ComplexInfinity`
raised a MethodError where `Base` returns `false` and the value itself for the
matching `Complex`, and `ℵ₀` took a rounding mode but not the keywords.

`float(::ComplexInfinity)` was a MethodError, the real infinities having got
theirs from the `AbstractFloat` conversion. #68 proposes `exp(im*angle(x))*Inf`,
which is unsound: `0 * Inf` is a `NaN`, so `float(ComplexInfinity())` gives
`Inf + NaN*im`, and the imaginary and negative real axes come back as diagonals.
`cospi`/`sinpi` are exact at the half-integers, so building the parts from them
keeps the axes exact. Two saturating parts can express only eight rays, so an
angle off them lands on the nearest one, which the test pins.

---------

Co-authored-by: Patrick Häcker <patrick.haecker@bosch.com>
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