classification
Title: Add math.cbrt() function: Cube Root
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.11
process
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: mark.dickinson Nosy List: AjithRamachandran, lemburg, mark.dickinson, rhettinger, serhiy.storchaka, stutzbach, vstinner
Priority: normal Keywords: patch

Created on 2021-06-09 07:45 by AjithRamachandran, last changed 2021-06-16 12:21 by mark.dickinson. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 26622 merged AjithRamachandran, 2021-06-09 08:16
Messages (13)
msg395388 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-06-09 07:58
See also issue27353.
msg395396 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-09 08:33
+1. This is part of C99, so if it's also supported by Visual Studio, then this seems like a no-brainer. If it's _not_ also supported by Visual Studio, or if there are implementations that have serious numerical problems (as was the case with fma) we'll need to write our own fallback implementation, which is less simple.

In principle it should be relatively easy for a math library to provide a correctly-rounded cbrt.
msg395399 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-09 09:02
BTW, to forestall questions about adding cmath.cbrt: it's not obvious how to extend the real cube root function to a complex cube root, so it might make sense to wait for actual use-cases before doing so.

The issue is that the most natural way to define a complex cube root would use a branch cut along the negative real axis (the same branch cut that cmath.sqrt and cmath.log use). But then the principal branch would *not* be an extension of math.cbrt: it would return non-real values on the negative real axis. (The *real* cube roots of negative numbers would fall on the two non-principal branches.)

See for example https://math.stackexchange.com/questions/71775/extending-the-cube-root-function-to-mathbbc
msg395405 - (view) Author: Ajith Ramachandran (AjithRamachandran) * Date: 2021-06-09 10:02
When I needed a function to find cube root, I couldn't find one in the math module. Also found the `cbrt()` function in C. I didn't account for the complex numbers. I didn't wanted to make it a complex function unnecessarily. So I used the `cbrt()` function directly and it was working well for my test cases. And it is supported by visual studio.
msg395411 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-06-09 11:39
I am surprised that this was not proposed before. Perhaps because it is so easy to write x**(1/3), and if you want a real root of negative argument, it is -(-x)**(1/3). It can be slightly less accurate that the result of C library function cbrt(), but good enough for any practical applications.

Ajith, was you aware of x**(1/3)?
msg395415 - (view) Author: Ajith Ramachandran (AjithRamachandran) * Date: 2021-06-09 12:01
Yes I was aware of x**(1/3) and used that. I just thought it would be useful if it was present in math module as it is also used in many situations like `sqrt()`.
msg395417 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-09 12:28
> Perhaps because it is so easy to write x**(1/3), and if you want a real root of negative argument, it is -(-x)**(1/3).

I consider `x**(1/3)` to be a trap and a bug magnet: many people won't realise that the correct spelling for their situation is actually `def f(x): return -((-x)**(1/3)) if x < 0 else x**(1/3)` and the failure mode of `x**(1/3)` for negative `x` seems suboptimal, in that it happily returns a complex number rather than raising a `ValueError` with a clear message indicating what was wrong. I've seen a good number of Stack Overflow questions from users confused about this exact thing.

And not only is that correct spelling unwieldy, but it _still_ doesn't do the right thing for `-0.0`, and if you care about getting that corner case right then the right spelling becomes even more unwieldy.

Of course, even with `math.cbrt` in the standard library people will still fall into the `x**(1/3)` trap, but at least we then have a decent replacement to point them to.
msg395418 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-09 12:29
There's also a decent chance that a libm implementation of cbrt will be correctly rounded, while `x**(1/3)` is highly unlikely to be so.
msg395420 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-06-09 13:50
I didn't know the "cbrt" function name. It seems like it exists in the libc, but also in numpy. So it's a good idea to reuse this name ;-)

numpy.cbrt(): Return the cube-root of an array, element-wise.
https://numpy.org/doc/stable/reference/generated/numpy.cbrt.html
msg395421 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-09 13:57
@Victor: Yep, the name is pretty standard. Not just C, but JavaScript, Java, R, and likely a lot of other languages that I haven't checked.
msg395430 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-09 15:49
If we *really* wanted to bikeshed on the name, back in 1991 Kahan wrote:

> Perhaps the last problem is the hardest: choosing the program's name. Ideally it should need no explanation, but a limitation upon its length may preclude that. Although "CBRT" has seen use, I prefer "QBRT" in order that the prefix "C" may be reserved for use with complex-valued functions.

Source: https://csclub.uwaterloo.ca/~pbarfuss/qbrt.pdf

But that was 30 years ago, and I think the "CBRT"-shaped ship has long since sailed.
msg395553 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-10 16:42
New changeset ac867f10b49322e25f34d2d8abd8e63c86834750 by Ajith Ramachandran in branch 'main':
bpo-44357:Add `math.cbrt()` function: Cube Root (GH-26622)
https://github.com/python/cpython/commit/ac867f10b49322e25f34d2d8abd8e63c86834750
msg395554 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-10 16:43
All done; closing. Thank you for the contribution!
History
Date User Action Args
2021-06-16 12:21:53mark.dickinsonsetassignee: mark.dickinson
2021-06-10 16:43:16mark.dickinsonsetstatus: open -> closed

messages: + msg395554
stage: patch review -> resolved
2021-06-10 16:42:17mark.dickinsonsetmessages: + msg395553
2021-06-09 15:49:31mark.dickinsonsetmessages: + msg395430
2021-06-09 13:57:58mark.dickinsonsetmessages: + msg395421
2021-06-09 13:50:49vstinnersetnosy: + vstinner
messages: + msg395420
2021-06-09 12:29:21mark.dickinsonsetmessages: + msg395418
2021-06-09 12:28:08mark.dickinsonsetmessages: + msg395417
2021-06-09 12:01:03AjithRamachandransetmessages: + msg395415
2021-06-09 11:39:17serhiy.storchakasetmessages: + msg395411
2021-06-09 10:02:27AjithRamachandransetmessages: + msg395405
2021-06-09 09:02:57mark.dickinsonsetmessages: + msg395399
2021-06-09 08:33:57mark.dickinsonsetmessages: + msg395396
2021-06-09 08:16:52AjithRamachandransetkeywords: + patch
stage: patch review
pull_requests: + pull_request25206
2021-06-09 07:58:30serhiy.storchakasetnosy: + stutzbach, serhiy.storchaka, rhettinger, lemburg, mark.dickinson

messages: + msg395388
versions: + Python 3.11, - Python 3.10
2021-06-09 07:45:49AjithRamachandrancreate