Skip to content

float**float return type #1406

Closed
Closed

Description

@gvanrossum

float**float is currently defined as returning float, but in Python 3, (negative float) ** (non-whole float) returns a complex number. (In Python 2 it raises ValueError.)

I'm not sure what to do about this -- in mypy a plugin won't be very effective, because typically the sign of the base (left operand) is not known statically (as opposed to the exponent, which is often a literal, which is how we addressed this for int**(negative int)).

Declaring the result a complex is technically correct (since float is effectively a subclass of complex, just like int is effectively a subclass of float), but probably going to be a pain. Making it Union[float, complex] is likely worse. Keeping it float is a lie and may lead to unanticipated errors, and making it Any (like we do for int**int when we don't know the sign of the exponent) a cop-out that just hides more errors.

All in all I think we should change this to complex, even if it's a pain. Any other opinions?

Activity

  1. JukkaL commented on Jun 14, 2017

    @JukkaL
    Contributor

    Making it Any or complex seem like the most reasonable options. For consistency with how int**int and open are annotated (they have Anys in their return types) we could make the return type Any.

  2. added a commit that references this issue on Sep 11, 2018
    146d291
  3. srittau commented on Sep 11, 2018

    @srittau
    Collaborator

    I would also slightly prefer returning complex instead of Any. This is mostly going to be used in mathematical applications that can be expected to handle this case. If it turns out to be a huge pain in practice, we can always change to Any later.

  4. added 3 commits that reference this issue on Dec 1, 2018
    ff94bf6
    7ac2fec
    a862513
  5. srittau commented on Dec 2, 2018

    @srittau
    Collaborator

    PR #2662, which implements this, causes CI to fail, due to problems with the sample code copied from the standard lib's random module. A fix for this is in python/mypy#5988. This is a good example of real world code that this would affect. I am still in favor of using a complex return type instead of Any, but I do not work with mathematical code a lot.

  6. utkarsh2102 commented on Dec 2, 2018

    @utkarsh2102
    Contributor

    I still did not understand the reason for CI failure :/

  7. srittau commented on Dec 2, 2018

    @srittau
    Collaborator

    Returning complex instead of float from __pow__() makes some implementations fail that assumed it to return float. We need to discuss whether that is acceptable or we need a different solution.

  8. utkarsh2102 commented on Dec 2, 2018

    @utkarsh2102
    Contributor

    Ah, okay.

  9. gvanrossum commented on Dec 3, 2018

    @gvanrossum
    MemberAuthor

    I would be unhappy if we changed this to -> complex. I think it's a design mistake that we made this work in Python 3 in the first place -- if you look at the math and cmath modules, the guiding principle there is that the functions in math never return complex results, only the ones in cmath do. The equivalent rule for ** would have been to preserve the Python 2 behavior, where (-1)**0.5 raises ValueError, and if you want a complex result you have to use e.g. complex(-1)**0.5. (Concrete example: math.sqrt(-1) raises ValueError, while cmath.sqrt(-1) returns 1j.)

    For the similar case of int/int, when we made this return float int Python 3, we did it differently: it always returns float, even if the result is mathematically an integer.

    I expect it would be hugely inconvenient if e.g. x**0.5 were to be inferred as a complex value -- it would encourage people to add useless asserts or # type: ignore comments or even casts just to make mypy understand that the expected result is a float (because x is known or assumed to be nonnegative).

  10. utkarsh2102 commented on Dec 3, 2018

    @utkarsh2102
    Contributor

    This makes sense.
    @srittau, should I change complex to Any then?

  11. gvanrossum commented on Dec 3, 2018

    @gvanrossum
    MemberAuthor

    Why not leave well enough alone? If someone uses a**b with negative a and expects a complex, let them rewrite it as complex(a)**b.

  12. utkarsh2102 commented on Dec 3, 2018

    @utkarsh2102
    Contributor

    As far as I understand, in #2662, I think there should only be an overload for when x is an int, rest should be as it was before. I hope I'm not missing anything here?

  13. srittau commented on Dec 3, 2018

    @srittau
    Collaborator

    I am a bit conflicted on this issue. On the one hand, returning float - while probably correct in the majority of cases - can hide bugs (but so can returning Any). On the other hand, as Guido said, returning complex is a big pain, as python/mypy#5988 shows. As someone who doesn't write much mathematical code, I'll defer to Guido's judgement.

    So basically, revert the changes the changes, no overload, just return float as before. But it would be helpful to add a comment to __pow__() that it returns complex if self is negative and x is not whole.

  14. srittau commented on Dec 3, 2018

    @srittau
    Collaborator

    Also, in my ongoing effort to promote AnyOf (python/typing#566), this would be a good use case. Same for range types (python/typing#554).

  15. utkarsh2102 commented on Dec 3, 2018

    @utkarsh2102
    Contributor

    Sure, on it.

  16. added a commit that references this issue on Dec 3, 2018
    c5f5af8
  17. added a commit that references this issue on Dec 3, 2018
    bee236f
  18. added a commit that references this issue on Jan 23, 2019
    b4b0e4a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions