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
complex() on object with __complex__ function loses sign of zero imaginary part #73788
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='https://github.com/mdickinson'closed_at=<Date2017-02-20.22:04:35.292>created_at=<Date2017-02-20.00:49:52.052>labels= ['interpreter-core', 'type-bug', '3.7']
title='complex() on object with __complex__ function loses sign of zero imaginary part'updated_at=<Date2017-03-31.16:36:13.628>user='https://bugs.python.org/TomKrauss'
CPython 2.7 and 3.5 have issues with the sign of zeroes even without any custom class:
>>> -(0j) # this is -(0+0j)
(-0-0j)
>>> (-0-0j) # but this equals to the difference between 0 and 0+0j
0j
>>> (-0.0-0j) # this is the difference between -0.0 and 0+0j
(-0+0j)
>>> -0j
-0j # <- on CPython 2.7
(-0-0j) # <- on CPython 3.5
It's unclear if the signs of the two potential zeroes in a complex number have a meaning, but the C standard considers these cases for all functions in the complex number's header.
Armin: that's a separate issue, and is expected behaviour.
>>> -(0j) # this is -(0+0j)
(-0-0j)
Yes: 0j is complex(0.0, 0.0); negating negates both the real and imaginary parts.
>>> (-0-0j) # but this equals to the difference between 0 and 0+0j
0j
This is an operation between an integer (note that the initial negation is a no-op) and a complex. Here the integer gets promoted to complex(0.0, 0.0), and we do complex(0.0, 0.0) - complex(0.0, 0.0), which gives complex(0.0, 0.0).
>>> (-0.0-0j) # this is the difference between -0.0 and 0+0j
(-0+0j)
This is complex(-0.0, 0.0) - complex(0.0, 0.0). The real and imaginary parts are operated on separately, and in keeping with IEEE 754, the real part is evaluated as -0.0 - 0.0, which is -0.0.
>>> -0j
-0j # <- on CPython 2.7
(-0-0j) # <- on CPython 3.5
The Python 3 behaviour here is correct. The Python 2 behaviour is the result of an unfortunate AST optimization designed to ensure that -<sys.maxint+1> is an int rather than a long.
It's unclear if the signs of the two potential zeroes in a complex number have a meaning
Yes, very much so. The signs are important in determining which side of a branch cut to use in the various cmath functions.
arigo commented on Feb 20, 2017
arigomannequin
Mannequin
More actions
Maybe I should be more explicit: what seems strange to me is that some complex numbers have a repr that, when entered in the source, produces a different result. For example, if you want the result (-0-0j) you have to enter something different. However, I missed the fact that calling explicitly complex(a, b) with a and b being floats always gives exactly a+bj with the correct signs. So I retract my comments.
IMHO it would be less surprising if repr(complex) would return 'complex(..., ...)': it would be possible to copy/paste and get the result value. I was bitten multiple time by the zero sign with complex numbers...
Armin, Victor: please open other issues for these discussions; they're unrelated to the bug reported here. Also, see bpo-27363, bpo-17336, bpo-22548, bpo-25839.
Proposed patch fixes constructing complex from complex.
But in future versions perhaps it is worth to disallow passing complex argument to two-arguments complex constructor.
arigo commented on Feb 20, 2017
arigomannequin
Mannequin
More actions
4 lines before the new "ci.real = cr.imag;", we have "cr.imag = 0.0; /* Shut up compiler warning */". The comment is now wrong: we really need to set cr.imag to 0.0.
Activity
TomKrauss commentedon Feb 20, 2017
Consider the following simple class that provides a "__complex__" method.
mdickinson commentedon Feb 20, 2017
Confirmed on my machine. The effect of the source is to do
complex(complex(x), 0.0)in this case, which isn't correct.mdickinson commentedon Feb 20, 2017
Looking at the source, there's also an issue with complex subclasses here:
arigo commentedon Feb 20, 2017
CPython 2.7 and 3.5 have issues with the sign of zeroes even without any custom class:
It's unclear if the signs of the two potential zeroes in a complex number have a meaning, but the C standard considers these cases for all functions in the complex number's header.
mdickinson commentedon Feb 20, 2017
Armin: that's a separate issue, and is expected behaviour.
Yes: 0j is complex(0.0, 0.0); negating negates both the real and imaginary parts.
This is an operation between an integer (note that the initial negation is a no-op) and a complex. Here the integer gets promoted to complex(0.0, 0.0), and we do complex(0.0, 0.0) - complex(0.0, 0.0), which gives complex(0.0, 0.0).
This is complex(-0.0, 0.0) - complex(0.0, 0.0). The real and imaginary parts are operated on separately, and in keeping with IEEE 754, the real part is evaluated as -0.0 - 0.0, which is -0.0.
The Python 3 behaviour here is correct. The Python 2 behaviour is the result of an unfortunate AST optimization designed to ensure that -<sys.maxint+1> is an int rather than a long.
None of the above is a new issue.
mdickinson commentedon Feb 20, 2017
Yes, very much so. The signs are important in determining which side of a branch cut to use in the various cmath functions.
arigo commentedon Feb 20, 2017
Maybe I should be more explicit: what seems strange to me is that some complex numbers have a repr that, when entered in the source, produces a different result. For example, if you want the result
(-0-0j)you have to enter something different. However, I missed the fact that calling explicitlycomplex(a, b)with a and b being floats always gives exactly a+bj with the correct signs. So I retract my comments.vstinner commentedon Feb 20, 2017
IMHO it would be less surprising if repr(complex) would return 'complex(..., ...)': it would be possible to copy/paste and get the result value. I was bitten multiple time by the zero sign with complex numbers...
mdickinson commentedon Feb 20, 2017
Armin, Victor: please open other issues for these discussions; they're unrelated to the bug reported here. Also, see bpo-27363, bpo-17336, bpo-22548, bpo-25839.
serhiy-storchaka commentedon Feb 20, 2017
Proposed patch fixes constructing complex from complex.
But in future versions perhaps it is worth to disallow passing complex argument to two-arguments complex constructor.
arigo commentedon Feb 20, 2017
4 lines before the new "ci.real = cr.imag;", we have "cr.imag = 0.0; /* Shut up compiler warning */". The comment is now wrong: we really need to set cr.imag to 0.0.
mdickinson commentedon Feb 20, 2017
complex-constructor-from-complex2.patch looks good to me.
What happens now? I presume we can no longer push to hg.python.org? So one of us needs to make a PR on GitHub and another review it?
serhiy-storchaka commentedon Feb 20, 2017
I pushed changes to my fork but when try to create a pull request it contains unrelated changes. Seems I do something wrong.
mdickinson commentedon Feb 20, 2017
Created #203, but I feel bad for having my name on the commits. :-(
mdickinson commentedon Feb 20, 2017
New changeset 112ec38 by GitHub in branch 'master':
bpo-29602: fix signed zero handling in complex constructor. (#203)
112ec38
mdickinson commentedon Feb 20, 2017
New changeset 4ddd897 by Mark Dickinson in branch 'bpo-29549-backport':
bpo-29602: fix signed zero handling in complex constructor
4ddd897
mdickinson commentedon Feb 20, 2017
New changeset c0b336e by GitHub in branch '2.7':
bpo-29602: fix signed zero handling in complex constructor (#204)
c0b336e
mdickinson commentedon Feb 20, 2017
New changeset 0936a00 by GitHub in branch '3.5':
bpo-29602: fix signed zero handling in complex constructor. (#203) (#205)
0936a00
mdickinson commentedon Feb 20, 2017
New changeset d9b3cdd by GitHub in branch '3.6':
bpo-29602: fix signed zero handling in complex constructor. (#203) (#206)
d9b3cdd
mdickinson commentedon Feb 20, 2017
I'm a bit puzzled about where the commit 4ddd897 came from, but as far as I can tell the changes to 2.7, 3.5, 3.6 and master are all okay.
All fixed. Thanks for the report!