Skip to content

Arithmetics with complex infinities is inconsistent with C/C++ #69639

Description

@FrancescoBiscani
BPO 25453
Nosy @malemburg, @tim-one, @mdickinson, @ericvsmith, @ezio-melotti, @stevendaprano, @asmeurer, @berkerpeksag

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 = None
closed_at = None
created_at = <Date 2015-10-21.12:20:43.550>
labels = ['interpreter-core', 'type-feature']
title = 'Arithmetics with complex infinities is inconsistent with C/C++'
updated_at = <Date 2016-05-16.21:29:52.095>
user = 'https://bugs.python.org/FrancescoBiscani'

bugs.python.org fields:

activity = <Date 2016-05-16.21:29:52.095>
actor = 'Aaron.Meurer'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Interpreter Core']
creation = <Date 2015-10-21.12:20:43.550>
creator = 'Francesco Biscani'
dependencies = []
files = []
hgrepos = []
issue_num = 25453
keywords = []
message_count = 15.0
messages = ['253283', '253284', '253286', '253289', '253290', '253293', '253295', '253296', '253297', '253301', '253304', '253305', '253306', '253308', '253337']
nosy_count = 11.0
nosy_names = ['lemburg', 'tim.peters', 'mark.dickinson', 'eric.smith', 'stutzbach', 'ezio.melotti', 'steven.daprano', 'Aaron.Meurer', 'berker.peksag', 'Francesco Biscani', 'Saksham Agrawal']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue25453'
versions = ['Python 3.6']

Linked PRs

Activity

  1. FrancescoBiscani commented on Oct 21, 2015

    FrancescoBiscanimannequin
    MannequinAuthor

    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:

    >>> complex(float('inf'),float('nan'))*2
    (nan+nanj)

    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:

    >>> complex(float('inf'),float('nan'))+2          
    (inf+nanj)

    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

    >>> 1./complex(float('inf'),float('nan'))  
    (nan+nanj)
  2. SakshamAgrawal commented on Oct 21, 2015

    SakshamAgrawalmannequin
    Mannequin

    Hi

    Would like to work on this bug...but I am new, would like some guidance

    Please help me

  3. ezio-melotti commented on Oct 21, 2015

    @ezio-melotti
    Member

    Saksham, if you would like to work on this issue you can check the devguide for more information.

  4. stevendaprano commented on Oct 21, 2015

    @stevendaprano
    Member

    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.

  5. FrancescoBiscani commented on Oct 21, 2015

    FrancescoBiscanimannequin
    MannequinAuthor

    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:

    • 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 :)

  6. SakshamAgrawal commented on Oct 21, 2015

    SakshamAgrawalmannequin
    Mannequin

    I read the first 6 chapters of the devguide.

    Now how should I proceed.

  7. ericvsmith commented on Oct 21, 2015

    @ericvsmith
    Member

    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.

  8. ericvsmith commented on Oct 21, 2015

    @ericvsmith
    Member

    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

  9. berkerpeksag commented on Oct 21, 2015

    @berkerpeksag
    Member

    Also, it would be best to take this discussion of how to produce a patch to the python-committers mailing list:

    or the core-mentorship list: https://mail.python.org/mailman/listinfo/core-mentorship :)

  10. ericvsmith commented on Oct 21, 2015

    @ericvsmith
    Member

    Berker Peksag added the comment:

    > Also, it would be best to take this discussion of how to produce a patch to the python-committers mailing list:

    or the core-mentorship list: https://mail.python.org/mailman/listinfo/core-mentorship :)

    Argh! That's of course what I meant. Apologies all around.

  11. mdickinson commented on Oct 21, 2015

    @mdickinson
    Member

    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.)

  12. mdickinson commented on Oct 21, 2015

    @mdickinson
    Member

    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. 13 remaining items

  14. added 4 commits that reference this issue on Jan 12, 2025
  15. skirpichev commented on Jan 14, 2025

    @skirpichev
    Member

    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.

  16. skirpichev commented on Jan 14, 2025

    @skirpichev
    Member
  17. removed their assignment
    on Jan 14, 2025
  18. hroncok commented on Jan 26, 2025

    @hroncok
    Contributor

    A test of gnofract4d fails because of this change:

    $ python3.13 -c 'from cmath import sin; print(sin(1-0j))'
    (0.8414709848078965+0j)
    
    $ python3.14 -c 'from cmath import sin; print(sin(1-0j))'
    (0.8414709848078965-0j)
    

    Notice the +0 vs -0 difference. Is this change deliberate or accidental?

  19. skirpichev commented on Jan 26, 2025

    @skirpichev
    Member

    That's coming from

    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.

  20. added 2 commits that reference this issue on Aug 16, 2025
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

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions