Siddharth
Siddharth

Reputation: 536

Replicate Python's complex-number-to-string conversion

import operator
>>> operator.truediv(-5.6, complex(-1, 0)) #eg1
(5.6-0j)
>>> operator.truediv(-5.6, complex(0, -1)) #eg2
(-0-5.6j)
>>> operator.truediv(-5.6, complex(0, 1)) #eg3
5.6j
>>> operator.truediv(-5.6, complex(1, 0)) #eg4
(-5.6+0j)
>>> operator.truediv(5.6, complex(0,-1)) #eg 5
(-0+5.6j)

Is there any way to know the exact rule of converting a complex number to its string representation as it appears in the Python interactive interpreter? Like some,

  1. In eg1 it prints 0j but in eg3 it does not.

  2. sometimes it prints 0 , -0 like in eg1 and eg4

  3. eg2 and eg5.

I know it doesn't make difference, but I want to implement the exactly same functionality as Python. I mean the output should be exactly the same.

Any help would be much appreciated. And it is not a homework problem, also thanks for reading.

Upvotes: 0

Views: 1303

Answers (2)

Cong Ma
Cong Ma

Reputation: 11302

If you want to implement another program to exactly replicate the current behaviour of the CPython interpreter, you can study the source code for complex number representation: static PyObject * complex_repr (PyComplexObject *v). This function is responsible for the default conversion of complex numbers to string.

Some of the gory details can be found in the function PyAPI_FUNC(char *) PyOS_double_to_string (double val, char format_code, int precision, int flags, int *type).

I don't think the exact interactive output for floating point numbers is part of the official Python language specification, so probably the source is the best guide here.


Edit: If you don't feel like reinventing the wheel, it's probably time to look into embedding Python in another application.

Upvotes: 2

Duncan
Duncan

Reputation: 95692

Python will always display the imaginary part of a complex number, because otherwise you could not distinguish from the output that it is indeed complex. This is similar to including the .0 on a floating point number even when it is exactly an integer.

The real part is displayed when it is not exactly +0. In the example you give the real part is being displayed because it is negative zero. Note that in floating point negative and positive zero have different representations even though they compare equal:

>>> float.fromhex('-0x0.0')
-0.0
>>> float.fromhex('0x0.0')
0.0
>>> float.fromhex('-0x0.0') == 0.0
True

This may not help much in emulating Python's output as you probably can't easily predict when Python will get a negative 0 in its result. For example:

>>> neg0 = float.fromhex('-0x0.0')
>>> neg0
-0.0
>>> neg0+0j
0j

The addition has normalised the real part to +0

>>> neg0*1j
(-0+0j)
>>> 0.0*(-1j)
-0j

multiplying by a positive imaginary keeps the negative sign on the real part, but multiplying by a negative imaginary doesn't make the real part negative.

But all of this is just implementation detail so can be different with different implementations of Python.

The fact that floating point has two separate representations for positive and negative zero is a detail of the IEEE 754 implementation of floating point. See https://en.wikipedia.org/wiki/Signed_zero

The part of the source code which detects whether or not to output the real part includes:

 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. */

The first part is true for both forms of zero. The second part is true only if the real part is not negative (copysign(x,y) gives the value of x with the sign of y, so copysign(1.0, negativezero) would be -1.

Upvotes: 2

Related Questions