On Mon, Mar 16, 2020, 3:41 AM Andrew Barnert via Python-ideas <python-ideas@python.org> wrote:
On Mar 15, 2020, at 22:37, Stephen J. Turnbull <turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
>
>
> Because they're not always available, even in 2020.  Also, ∞ is
> ambiguous; it's used for the ordinal number infinity (IIRC, more
> precisely denoted ω), the cardinal number infinity, the positive limit
> of the real line, the antipode of 0 in the complex (Riemannian)
> sphere, and probably other things.

Well, there are an infinite number of ever larger infinite ordinals, ω or ω_0 being the first one, and likewise an infinite number of infinite cardinal, aleph_0 being the first one, and people rarely use the ∞ symbol for any of them.

But people definitely do use the ∞ symbol for the projective infinity (the single point added to the real line to create the projective circle), and its complex equivalent (the single point added to the complex plan to give you the Riemann sphere). And IEEE (and therefore Python) infinity definitely doesn’t mean that; it explicitly has separate positive and negative infinities, modeling the affine rather than projective extension of the reals (the positive and negative limits of the real line).

There are a few different obvious ways you could build an IEEE-float-style complex out of IEEE floats, but the one that C99 and C++ both use is probably the simplest: just model then as the Cartesian product of IEEE float with itself, applying the usual arithmetic rules over IEEE float.

And that means these odd things make sense:

>>>> complex("inf")
> (inf+0j)
>
> Oof. ;-)

What else would you expect? The projective real line (circle) multiplied by itself gives you the projective complex plane (sphere) with a single infinity opposite 0, but the affine real line multiplied by itself gives you an infinite number of infinities. You can look at these as the limits of every line in complex space, or as the “circle” in polar coordinates with infinite distance at every angle, or as the “square” in cartesian coordinates made up of positive and negative real infinity with every imaginary number and positive and negative imaginary infinity with every real number. When you’re dealing with a discrete approximation like IEEE floats, these three are all different, but the last one falls out naturally from the definition, so that’s what Python does—and C, C++, and lots of other languages.

So, inf+0j is one real-positive-infinite number, but inf+1j is another, and there’s a whole slew of additional ones (one for each float value for the imaginary component).

>>>> complex("inf") * 1j
> (nan+infj)

(a+b)(c+d) = ac + ad + bc + bd
(a+bj)(c+dj) = (ac - bd) + (ad + bc)j
(inf+0j)(0+1j) = (inf*0 - 0*1) + (inf*1 + 0*0)j

And inf*0 is nan, while inf*1 is inf, right?

Here’s the fun bit:

    >>> cmath.isinf(_)
    True

Again, Python, C, and lots of other languages agree here, and it makes sense once you think about it. We have a number that’s either indeterminate or multivalued or unknown on one axis, but it’s infinite on the other axis, so whatever value(s) it may represent, they all must be infinite.

If you look at the values you get from other complex arithmetic and the other functions in the cmath library, they all should make sense according to the same model, and should agree with C99. (I haven’t actually tested that…)

It's convenient to be compatible with cmath. CAS systems vary in regards to whether it's inappropriate and over-reductionistic to just throw away a scalar magnitude 'times' an infinity symbol.

These approach ±inf at different rates:
-2/n
1/n
2/n

It's not a pedantic differentiation; but my guess is that such use cases generally call for CAS and not cmath or python anyway.

...

Again, could the locale module be extended to handle customizable formatting for inf?