classification
Title: Documentation for pow() should include the possibility of complex numbers as a return type
Type: enhancement Stage:
Components: Documentation Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Dennis Sweeney, docs@python, eyadams, mark.dickinson, steven.daprano
Priority: normal Keywords:

Created on 2021-06-08 04:13 by eyadams, last changed 2021-06-09 08:52 by steven.daprano.

Messages (3)
msg395305 - (view) Author: Erik Y. Adams (eyadams) Date: 2021-06-08 04:13
https://docs.python.org/3/library/functions.html#pow

The built-in pow() function will return a complex number if the base is negative and the exponent is a float between 0 and 1. For example, the value returned by `pow(-1, 1.0/3)` is `(1.0000000000000002+1.7320508075688772j)`

The answer is mathematically correct, but `-2.0` is also mathematically correct. There is nothing in the documentation currently to suggest that a complex number might be returned; in fact, given the statement "[with] mixed operand types, the coercion rules for binary arithmetic operators apply", one might reasonably expect `-2.0` as the answer. 

I suggest the following sentences be added to the end of the second paragraph:

"If `base` is negative and the `exp` is a `float` between 0 and 1, a complex number will be returned. For example, `pow(-8, 1.0/3)` will return `(1.0000000000000002+1.7320508075688772j)`, and not `-2.0.`"
msg395308 - (view) Author: Dennis Sweeney (Dennis Sweeney) * Date: 2021-06-08 05:56
For some prior art, https://www.wolframalpha.com/input/?i=%28-8%29+%5E+%281%2F3%29 says it defaults to using "the principal root" over "the real-valued root"

Also, I think the relevant property is that the exponent is not an integer; being between 0 and 1 is irrelevant:

>>> pow(-8, 4/3)
(-8.000000000000005-13.856406460551014j)

Maybe the tweak could be something like

"Note that using a negative base with a non-integer exponent will return the principal complex exponent value, even if a different real value exists."
msg395339 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-08 16:20
[Dennis]

> I think the relevant property is that the exponent is not an integer

Yep: the delegation to complex pow kicks in after handling infinities and nans, and only for strictly negative base (-0.0 doesn't count as negative for this purpose) and non-integral exponent.

Here's the relevant code: https://github.com/python/cpython/blob/257e400a19b34c7da6e2aa500d80b54e4c4dbf6f/Objects/floatobject.c#L773-L782

To avoid confusion, we should probably not mention fractions like `1/3` and `4/3` as example exponents in the documentation, since those hit the What-You-See-Is-Not-What-You-Get nature of binary floating-point. Mathematically, `z^(1/3)` is a very different thing from `z^(6004799503160661/18014398509481984)` for a negative real number `z`, and the latter is what's _actually_ being computed with `z**(1/3)`. The advantage of the principal branch approach is that it's continuous in the exponent, so that `z^(1/3)` and `z^(6004799503160661/18014398509481984)` only differ by a tiny amount.
History
Date User Action Args
2021-06-09 08:52:14steven.dapranosetnosy: + steven.daprano
2021-06-08 16:20:04mark.dickinsonsetnosy: + mark.dickinson
messages: + msg395339
2021-06-08 05:56:47Dennis Sweeneysetnosy: + Dennis Sweeney
messages: + msg395308
2021-06-08 04:13:36eyadamscreate