On Mon, Feb 21, 2022 at 1:39 PM Tim Peters <tim.pet...@gmail.com> wrote:

> [Mark Dickinson <dicki...@gmail.com>]
> > It Would Be Nice If
> > `-1 * complex(inf, 0.0)` gave `complex(-inf, -0.0)` instead
> > of the current result of `complex(-inf, nan)`.


> Except replacing -1 with "-1.0" or "complex(-1)" would
> presumably   _still_ return complex(-inf, nan), despite that
>
> >>> -1 == -1.0 == complex(-1)
> True
>

I think Python should do what C99 and C++ do, which is define complex(a, b)
* c to mean complex(a * c, b * c). Python's complex constructor accepts two
complex arguments, so that definition works even in the complex * complex
case, though in practice you'd want to optimize for common argument types
and defer to __rmul__ if the second argument is of unknown type.

It does have the consequence that values that compare equal can have
different arithmetic behavior. That happens in IEEE arithmetic with ±0, and
it's a good thing in certain cases. IEEE arithmetic deliberately preserves
the sign of 0 where it can. It's not an accident of the representation.

There is a difference between a float and a complex with ±0 imaginary part
that justifies the different answers in this case: the former is real by
construction, while the latter may have underflowed. It's unfortunate that
underflowed values compare equal to true zero, but there's a reason they
say you shouldn't compare floating-point numbers for equality.

If that's wanted, better for  complex.__mul__
> to detect on its own whether component parts are 0,
> and use a simpler multiplication implementation if so.
>

I think it's better not to do that for the reason in the previous paragraph.

For example, this similar surprise has nothing to do with type promotion:
>
> >>> 1j * complex(math.inf, -0.0)
> (nan+infj)
>

1j is, in effect, being prematurely promoted to complex because Python
lacks an imaginary type. C99 has _Imaginary for this reason.

Another consequence of the missing imaginary type is that you can't write
negative zeros in complex literals consistently.

C++ doesn't have std::imaginary, possibly because it has no imaginary
literals.

That, plus I'm still waiting for a plausible use case ;-)
>

Why are complex numbers in core Python in the first place? I'm not sure,
but I think it's for the same reason as Ellipsis and binary @ and the third
argument to slice: to support numpy, since numpy can't define its own
syntax. The core devs wouldn't normally add syntax just for some
third-party library, but numpy is so important that they bend the rules.

People use numpy to do heavy-duty real-world number crunching. The weird
IEEE corner cases actually affect the stability of these calculations;
that's why the IEEE standard tried to pin down their behavior. I think that
improving Python's built-in numerics would have benefits for numpy users in
the form of fewer failed computations (mysteriously failed for the many
that don't have the numeric-analysis chops to work out what went wrong). I
think it would have strategic value. It's less noticeable than adding
syntax, but also easier.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/5UTUOQ73XOIMDHQZM3GSQH3XMLPEN7XA/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to