This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: negative zero components are ignored in complex number literals
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.4, Python 2.7
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Mark Lundeberg, mark.dickinson, vstinner
Priority: normal Keywords:

Created on 2015-12-11 08:44 by Mark Lundeberg, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (6)
msg256209 - (view) Author: Mark Lundeberg (Mark Lundeberg) Date: 2015-12-11 08:44
Although -0.0 and +0.0 compare as equal using the == operator, they are distinct floating point numbers and in some cases behave differently. (See more information on the wikipedia article "Signed zero".) The distinction between +0.0 and -0.0 is most important in complex arithmetic, for example it is conventional and useful that sqrt(-1+0i) ==> +i and sqrt(-1-0i) ==> -i. Python currently allows the floating point number -0.0 to be entered as a literal:

>>> -0.0

Complex floating point numbers in python also can hold negative zero components, as shown in their repr()

>>> -(1+0j)

However they cannot be input directly as literals; it is currently necessary to use the above construction. Unfortunately the output of the repr() cannot be used as a string literal to obtain the same number:

>>> (-1-0j)

except, in contrast:

>>> complex('-1-0j')

The literal -1-0j should yield a complex number with negative zero imaginary part. Note also that complex literals with negative zero real parts have the same bug, e.g. -0+1j is not the same as -(0-1j)
msg256210 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-12-11 08:53
You should use complex(a, b) to have a reliable behaviour.

Python parse doesn't see "-1-0j" as a complex literal, but as (-1)-(0j): int-complex. Example with the AST output:

>>> ast.dump(ast.parse('-1-0j'))
'Module(body=[Expr(value=BinOp(left=UnaryOp(op=USub(), operand=Num(n=1)), op=Sub(), right=Num(n=0j)))])'

It looks like complex has the same behaviour than float:

>>> x=-0.0; x=0+x; x.real
>>> x=-0.0; x=0-x; x.real
>>> x=complex(0.0, -0.0); x=0+x; (x.real, x.imag)
(0.0, 0.0)
>>> x=complex(0.0, -0.0); x=0-x; (x.real, x.imag)
(0.0, 0.0)

zero sign is lost on int+complex, int-complex, int+complex, int-complex.
msg256211 - (view) Author: Mark Lundeberg (Mark Lundeberg) Date: 2015-12-11 09:01
Good point, it is doing (int-complex), observe also the following pecularities:

>>> -0 - 0j
>>> -0. - 0j
>>> -0j
>>> 0-0j
>>> -(0j)
>>> 0.+(-0j)

Does this mean the bug is in repr() ? As I understand the output of repr() is supposed to be something that can evaluated to recreate the same object. However I am unsure whether it would be nicer if repr() were to yield 'complex(-0.,-0.)' or '-(-0.+0j)'.
msg256215 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-12-11 14:43
This is something that comes up repeatedly on the bug tracker. There's no bug here in the complex type or the repr. What there *is* is a limitation resulting from the fact that Python doesn't have *imaginary* literals, only *complex* literals. So in:


the 0j is already a complex number with both real and imaginary parts equal to 0.0. Then -1 gets promoted to a complex number with real part -1 and imaginary part 0.0.  And now you're doing:

complex(-1.0, 0.0) - complex(0.0, 0.0)

which naturally gives an imaginary part of +0.0 rather than 0.0.

You'll see the same issue in C: there was an attempt to fix it in C99 by introducing Imaginary types, but those Imaginary types haven't been widely adopted. The most recent reincarnation of the C standard finally introduces a macro that lets you instantiate a complex number in terms of its real and imaginary components (instead of doing real_part + imag_part * I); this is something that Python already has in the form of the complex constructor.

Closing as not a bug.
msg256216 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-12-11 14:45
> As I understand the output of repr() is supposed to be something that can evaluated to recreate the same object.

Right, but that's an ideal that's not always achieved in practice. If I had my druthers, I'd 'fix' the repr of the complex object to return something that's written in terms of the constructor (for example, "complex(2.3, -0.0)"). I don't think that's a reasonable change from the POV of backwards compatibility though.
msg256217 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2015-12-11 14:48
Previous discussions: #17336, #22548
Date User Action Args
2022-04-11 14:58:24adminsetgithub: 70026
2015-12-11 14:48:51mark.dickinsonsetmessages: + msg256217
2015-12-11 14:45:35mark.dickinsonsetmessages: + msg256216
2015-12-11 14:43:14mark.dickinsonsetstatus: open -> closed
resolution: not a bug
messages: + msg256215
2015-12-11 09:01:36Mark Lundebergsetmessages: + msg256211
2015-12-11 08:53:10vstinnersetnosy: + mark.dickinson, vstinner
messages: + msg256210
2015-12-11 08:44:59Mark Lundebergcreate