Title: Discrepancy between math.pow(0.0, -inf) and 0.0**-inf
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.11
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: mark.dickinson, rhettinger, serhiy.storchaka, steven.daprano, tim.peters
Priority: normal Keywords: patch

Created on 2021-06-07 19:13 by mark.dickinson, last changed 2021-06-12 09:27 by mark.dickinson. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 26606 merged mark.dickinson, 2021-06-08 17:04
Messages (6)
msg395277 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-07 19:13
For floats x and y, x ** y and math.pow(x, y) mostly agree. There are three points of difference:

1. if x is finite and negative and y is finite and non-integral, then x ** y returns a complex number, while math.pow(x, y) raises ValueError
2. for cases where x ** y raises ZeroDivisionError (for example, 0.0 ** -1.0), math.pow(x, y) raises ValueError instead
3. the special cases 0.0 ** -inf and (-0.0) ** -inf return inf, but the equivalent math.pow calls raise ValueError

That third discrepancy is a surprising one: in particular, it's the only case where math.pow does not follow IEEE 754 rules.

Note that the math.pow behaviour is not accidental. The special cases were based on C99 Annex F (and are documented to do so), but the standards themselves have evolved here. In chronological order:

- IEEE 754-1985 did not cover transcendental functions, so has nothing to say on the topic of special values for pow.
- C99 §F.9.4.4 implies that pow(0, -inf) should raise the "divide-by-zero" exception; the relevant clause covers pow(0, y) for any y satisfying y < 0 and y not an odd integer
- IEEE 754-2008 §9.2.1 has an explicit clause specifying that pow(0, -inf) should be inf and that the operation does not raise any exception.
- C11 §F.10.4.4 mentions the case pow(0, -inf) explicitly and now says "may raise the divide-by-zero floating-point exception". The "may" is significant: other clauses simply say "raises the "divide-by-zero" floating-point exception".
- IEEE 754-2019 §9.2.1 is unchanged from IEEE 754-2008 for this particular special case.
- Similarly, C17 is unchanged from C11 in this respect.

For Python 3.11, I propose changing the behaviour of math.pow in this corner case so that it returns inf instead of raising. This would make math.pow conformant with IEEE 754, consistent with C11 and later, and consistent with the built-in pow function.
msg395303 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2021-06-08 03:06
+1. Although, to be fair, I'd personally be happy if (+-0)**inf returned, say, 1.375 instead ;-)
msg395307 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2021-06-08 05:05
+1 from me as well.
msg395312 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-06-08 07:47
What about math.pow(0.0, -1.0)? Should it return -inf or raise ZeroDivisionError?

And what about math.pow(-0.0, -inf)? Should it return inf, -inf or nan or raise an exception?
msg395313 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-08 07:52
> What about math.pow(0.0, -1.0)? Should it return -inf or raise ZeroDivisionError?

Neither: it should raise ValueError, as it does now. This matches IEEE 754 along with the math module's mapping of IEEE 754 floating-point exceptions to Python exceptions.

> And what about math.pow(-0.0, -inf)?

This should return inf, the same as math.pow(0.0, -inf). The relevant clauses of the C standard and IEEE 754 cover both these cases; I should have written `math.pow(±0.0, -inf)` throughout my previous message.
msg395682 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-06-12 09:23
New changeset 4a42cebf6dd769e2fa4e234a9e91093b3ad1cb63 by Mark Dickinson in branch 'main':
bpo-44339: Fix math.pow corner case to comply with IEEE 754 (GH-26606)
Date User Action Args
2021-06-12 09:27:42mark.dickinsonsetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2021-06-12 09:23:08mark.dickinsonsetmessages: + msg395682
2021-06-09 08:51:43steven.dapranosetnosy: + steven.daprano
2021-06-08 17:04:13mark.dickinsonsetkeywords: + patch
stage: patch review
pull_requests: + pull_request25189
2021-06-08 07:52:22mark.dickinsonsetmessages: + msg395313
2021-06-08 07:47:57serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg395312
2021-06-08 05:05:49rhettingersetnosy: + rhettinger
messages: + msg395307
2021-06-08 03:06:14tim.peterssetnosy: + tim.peters
messages: + msg395303
2021-06-07 19:14:52mark.dickinsonsettype: enhancement
components: + Library (Lib)
2021-06-07 19:13:36mark.dickinsoncreate