Issue 25839: negative zero components are ignored in complex number literals (original) (raw)

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 -0.0 Complex floating point numbers in python also can hold negative zero components, as shown in their repr() >>> -(1+0j) (-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) (-1+0j) except, in contrast: >>> complex('-1-0j') (-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 0.0 >>> x=-0.0; x=0-x; x.real 0.0 >>> 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 0j >>> -0. - 0j (-0+0j) >>> -0j -0j >>> 0-0j 0j >>> -(0j) (-0-0j) >>> 0.+(-0j) 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: -1-0j 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
History
Date User Action Args
2022-04-11 14:58:24 admin set github: 70026
2015-12-11 14:48:51 mark.dickinson set messages: +
2015-12-11 14:45:35 mark.dickinson set messages: +
2015-12-11 14:43:14 mark.dickinson set status: open -> closedresolution: not a bugmessages: +
2015-12-11 09:01:36 Mark Lundeberg set messages: +
2015-12-11 08:53:10 vstinner set nosy: + mark.dickinson, vstinnermessages: +
2015-12-11 08:44:59 Mark Lundeberg create