I am wondering about the way Python (3.3.0) prints complex numbers. I am looking for an explanation, not a way to change the print.
Example:
>>
The answer lies in the Python source code itself.
I'll work with one of your examples. Let
a = complex(0,1)
b = complex(-1, 0)
When you doa*b
you're calling this function:
real_part = a.real*b.real - a.imag*b.imag
imag_part = a.real*b.imag + a.imag*b.real
And if you do that in the python interpreter, you'll get
>>> real_part
-0.0
>>> imag_part
-1.0
From IEEE754, you're getting a negative zero, and since that's not +0, you get the parens and the real part when printing it.
if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
/* Real part is +0: just output the imaginary part and do not
include parens. */
...
else {
/* Format imaginary part with sign, real part without. Include
parens in the result. */
...
I guess (but I don't know for sure) that the rationale comes from the importance of that sign when calculating with elementary complex functions (there's a reference for this in the wikipedia article on signed zero).
0j
is an imaginary literal which indeed indicates a complex number rather than an integer or floating-point one.
The +-0
("signed zero") is a result of Python's conformance to IEEE 754 floating point representation since in Python, complex is by definition a pair of floating point numbers. Due to the latter, there's no need to print or specify zero fraction parts for a complex
too.
The -0
part is printed in order to accurately represent the contents as repr()'s documentation demands (repr()
is implicitly called whenever an operation's result is output to the console).
Regarding the question why (-0+1j) = 1j
but (1j*-1) = (-0+1j)
.
Note that (-0+0j)
or (-0.0+0j)
aren't single complex numbers but expressions - an int
/float
added to a complex
. To compute the result, first the first number is converted to a complex
(-0
-> (0.0,0.0)
since integers don't have signed zeros, -0.0
-> (-0.0,0.0)
). Then its .real
and .imag
are added to the corresponding ones of 1j
which are (+0.0,1.0)
. The result is (+0.0,1.0)
:^) . To construct a complex directly, use complex(-0.0,1)
.
It prints 0j
to indicate that it's still a complex
value. You can also type it back in that way:
>>> 0j
0j
The rest is probably the result of the magic of IEEE 754 floating point representation, which makes a distinction between 0 and -0, the so-called signed zero. Basically, there's a single bit that says whether the number is positive or negative, regardless of whether the number happens to be zero. This explains why 1j * -1
gives something with a negative zero real part: the positive zero got multiplied by -1.
-0 is required by the standard to compare equal to +0, which explains why (1j * -1).real == 0.0
still holds.
The reason that Python still decides to print the -0, is that in the complex world these make a difference for branch cuts, for instance in the phase function:
>>> phase(complex(-1.0, 0.0))
3.141592653589793
>>> phase(complex(-1.0, -0.0))
-3.141592653589793
This is about the imaginary part, not the real part, but it's easy to imagine situations where the sign of the real part would make a similar difference.
As far as the first question is concerned: if it just printed 0
it would be mathematically correct, but you wouldn't know you were dealing with a complex
object vs an int
. As long as you don't specify .real
you will always get a J component.
I'm not sure why you would ever get -0
; it's not technically incorrect (-1 * 0 = 0)
but it's syntactically odd.
As far as the rest goes, it's strange that it isn't consistent, however none are technically correct, just an artifact of the implementation.