You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Arithmetics with complex infinities is inconsistent with C/C++ #69639
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2015-10-21.12:20:43.550>labels= ['interpreter-core', 'type-feature']
title='Arithmetics with complex infinities is inconsistent with C/C++'updated_at=<Date2016-05-16.21:29:52.095>user='https://bugs.python.org/FrancescoBiscani'
This happens because complex multiplication in Python is implemented in the most straightforward way, but the presence of a nan component "infects" both components of the result and leads to a complex nan result. See also how complex multiplication is implemented in Annex G.5.1.6 of the C99 standard.
It feels wrong that a complex infinity multiplied by a real number results in a complex nan. By comparison, the result given here by C/C++ is inf+nan*j.
That is, addition has a different behaviour because it does not require mixing up the components of the operands.
This behaviour has unexpected consequences when one interacts with math libraries implemented in C/C++ and accessed via Python through some wrapping tool. For instance, whereas 1./(inf+nan*j) is zero in C/C++, it becomes in Python
I'm not entirely satisfied that the way it is calculated by C++11/C99 is correct. (Although I can see the appeal of the C version.) Mathematically, complex multiplication (a+bj)x should be identical to (a+bj)(x+0j), but obviously in the presence of NANs that is no longer the case. So it isn't clear to me that Python is wrong to allow NANs to "infect" the real part after multiplication.
Before changing the behaviour, I'd like to hear from someone who might be able to comment on what the IEEE-754 standard may have to say about this.
FrancescoBiscani commented on Oct 21, 2015
FrancescoBiscanimannequin
MannequinAuthor
More actions
The best reference I could find so far is in the C99 standard:
Annex G is titled "IEC 60559-compatible complex arithmetic". In G.3.1 it is written:
"""
A complex or imaginary value with at least one infinite part is regarded as an infinity (even if its other part is a NaN).
"""
Later on, in G.5.1.4, it is stated:
"""
The * and / operators satisfy the following infinity properties for all real, imaginary, and complex operands:
if one operand is an infinity and the other operand is a nonzero finite number or an infinity, then the result of the * operator is an infinity;
if the first operand is an infinity and the second operand is a finite number, then the result of the / operator is an infinity;
if the first operand is a finite number and the second operand is an infinity, then the result of the / operator is a zero;
"""
So to recap, according to these definitions:
inf + nanj is a complex infinity,
(inf + nanj) * (2 + 0j) should give a complex infinity (i.e., a complex number in which at least one component is inf).
I am not saying that these rules are necessarily "mathematically correct". I am aware that floating point is always a quagmire to get into, and the situation here is even more complex (eh!) than usual.
But it seems to me that Python, or CPython at least, follows a pattern of copying (or relying on) the behaviour of C for floating-point operations, and it seems like in the case of complex numbers this "rule" is broken. It certainly caught me by surprise anyway :)
Saksham: First we need our "experts" to decide what, if any, change is needed. If we decide that a change is needed, at that point we'd look at a patch.
If the proposal is to add C99 Appendix G-style handling of nan+nanj results for complex multiplication, that doesn't seem unreasonable to me, though I'm not particularly convinced that the gain in complexity is worth it. So -0 from me. Whatever happens, I'd hope that the complex multiplication operation
remains simple and easily explainable, not degenerating into a mass of special cases.
I'd be opposed to adding special-case handing for float-by-complex multiplication (i.e., doing anything other than promoting the float to a complex by introducing a zero imaginary part, and then doing normal complex multiplication). But I don't think that's what Francesco is proposing.
FWIW, NumPy semantics follow those of Python, though that's hardly an independent data point.
In answer to Steven, IEEE 754 has precisely nothing to say on this subject: it simply doesn't cover complex arithmetic.
Francesco: by the way, I'd be interested to see the C source that gave you inf + nan*j for this. How did you initialise the arguments to the multiplication? C99 has a significant issue here (fixed in C2011), namely that there's no standard way to create a complex value with given real and imaginary parts. (Using real_part + I*imaginary_part doesn't count, because in the absence of the Imaginary type, which few compilers seem to implement, the real part of the "I" constant mucks up the arithmetic w.r.t. infinities, NaNs and signed zeros.)
Changing Python versions and issue type: the current behaviour is certainly deliberate, so a proposal to change it would be a feature request, which could only land in Python 3.6 or later.
Python 3.13.1 (tags/v3.13.1:0671451779, Dec 4 2024, 07:55:26) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1-0j
(1+0j)
vs
Python 3.14.0a4+ (heads/main:0ef8d470b7, Jan 26 2025, 09:13:19) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1-0j
(1-0j)
Pre-3.14 code smash imaginary negative zero, now it's preserved. So, you get sin(complex(1, -0.0)) ~ complex(0.8, -0.0). You can get stable results across all CPython versions with z=complex(1, 0)=1+0j.
Activity
FrancescoBiscani commentedon Oct 21, 2015
The C++11/C99 standards define a complex infinity as a complex number in which at least one of the components is inf. Consider the Python snippet:
This happens because complex multiplication in Python is implemented in the most straightforward way, but the presence of a nan component "infects" both components of the result and leads to a complex nan result. See also how complex multiplication is implemented in Annex G.5.1.6 of the C99 standard.
It feels wrong that a complex infinity multiplied by a real number results in a complex nan. By comparison, the result given here by C/C++ is inf+nan*j.
Note also this:
That is, addition has a different behaviour because it does not require mixing up the components of the operands.
This behaviour has unexpected consequences when one interacts with math libraries implemented in C/C++ and accessed via Python through some wrapping tool. For instance, whereas 1./(inf+nan*j) is zero in C/C++, it becomes in Python
SakshamAgrawal commentedon Oct 21, 2015
Hi
Would like to work on this bug...but I am new, would like some guidance
Please help me
ezio-melotti commentedon Oct 21, 2015
Saksham, if you would like to work on this issue you can check the devguide for more information.
stevendaprano commentedon Oct 21, 2015
I'm not entirely satisfied that the way it is calculated by C++11/C99 is correct. (Although I can see the appeal of the C version.) Mathematically, complex multiplication (a+bj)x should be identical to (a+bj)(x+0j), but obviously in the presence of NANs that is no longer the case. So it isn't clear to me that Python is wrong to allow NANs to "infect" the real part after multiplication.
Before changing the behaviour, I'd like to hear from someone who might be able to comment on what the IEEE-754 standard may have to say about this.
FrancescoBiscani commentedon Oct 21, 2015
The best reference I could find so far is in the C99 standard:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
Annex G is titled "IEC 60559-compatible complex arithmetic". In G.3.1 it is written:
"""
A complex or imaginary value with at least one infinite part is regarded as an infinity (even if its other part is a NaN).
"""
Later on, in G.5.1.4, it is stated:
"""
The * and / operators satisfy the following infinity properties for all real, imaginary, and complex operands:
"""
So to recap, according to these definitions:
I am not saying that these rules are necessarily "mathematically correct". I am aware that floating point is always a quagmire to get into, and the situation here is even more complex (eh!) than usual.
But it seems to me that Python, or CPython at least, follows a pattern of copying (or relying on) the behaviour of C for floating-point operations, and it seems like in the case of complex numbers this "rule" is broken. It certainly caught me by surprise anyway :)
SakshamAgrawal commentedon Oct 21, 2015
I read the first 6 chapters of the devguide.
Now how should I proceed.
ericvsmith commentedon Oct 21, 2015
Saksham: First we need our "experts" to decide what, if any, change is needed. If we decide that a change is needed, at that point we'd look at a patch.
ericvsmith commentedon Oct 21, 2015
Saksham: Also, it would be best to take this discussion of how to produce a patch to the python-committers mailing list: https://mail.python.org/mailman/listinfo/python-committers
berkerpeksag commentedon Oct 21, 2015
or the core-mentorship list: https://mail.python.org/mailman/listinfo/core-mentorship :)
ericvsmith commentedon Oct 21, 2015
Argh! That's of course what I meant. Apologies all around.
mdickinson commentedon Oct 21, 2015
If the proposal is to add C99 Appendix G-style handling of nan+nanj results for complex multiplication, that doesn't seem unreasonable to me, though I'm not particularly convinced that the gain in complexity is worth it. So -0 from me. Whatever happens, I'd hope that the complex multiplication operation
remains simple and easily explainable, not degenerating into a mass of special cases.
I'd be opposed to adding special-case handing for float-by-complex multiplication (i.e., doing anything other than promoting the float to a complex by introducing a zero imaginary part, and then doing normal complex multiplication). But I don't think that's what Francesco is proposing.
FWIW, NumPy semantics follow those of Python, though that's hardly an independent data point.
In answer to Steven, IEEE 754 has precisely nothing to say on this subject: it simply doesn't cover complex arithmetic.
Francesco: by the way, I'd be interested to see the C source that gave you
inf + nan*jfor this. How did you initialise the arguments to the multiplication? C99 has a significant issue here (fixed in C2011), namely that there's no standard way to create a complex value with given real and imaginary parts. (Using real_part + I*imaginary_part doesn't count, because in the absence of the Imaginary type, which few compilers seem to implement, the real part of the "I" constant mucks up the arithmetic w.r.t. infinities, NaNs and signed zeros.)mdickinson commentedon Oct 21, 2015
Changing Python versions and issue type: the current behaviour is certainly deliberate, so a proposal to change it would be a feature request, which could only land in Python 3.6 or later.
13 remaining items
pythongh-69639: Add mixed-mode rules for complex arithmetic (C-like) (p…
pythongh-69639: Add mixed-mode rules for complex arithmetic (C-like) (p…
pythongh-69639: Add mixed-mode rules for complex arithmetic (C-like) (p…
pythongh-69639: Add mixed-mode rules for complex arithmetic (C-like) (p…
skirpichev commentedon Jan 14, 2025
The #124829 (mixed arithmetic for complexes and floats) was merged.
Any further progress (i.e. imaginary type) will require a PEP. So, meanwhile I'll close this as completed.
skirpichev commentedon Jan 14, 2025
JFR: https://discuss.python.org/t/77073
hroncok commentedon Jan 26, 2025
A test of gnofract4d fails because of this change:
Notice the +0 vs -0 difference. Is this change deliberate or accidental?
skirpichev commentedon Jan 26, 2025
That's coming from
vs
Pre-3.14 code smash imaginary negative zero, now it's preserved. So, you get
sin(complex(1, -0.0)) ~ complex(0.8, -0.0). You can get stable results across all CPython versions withz=complex(1, 0)=1+0j.pythongh-69639: Add mixed-mode rules for complex arithmetic (C-like) (p…
pythongh-69639: Add mixed-mode rules for complex arithmetic (C-like) (p…
Fix Pow/Pow2 eigvals branch cut on Python 3.14
Fix Pow/Pow2 eigvals branch cut on Python 3.14 (PennyLaneAI#10139)
Fix Pow/Pow2 eigvals branch cut on Python 3.14 (#10139)