[Numpy-discussion] Change in the representation of complex numbers in NumPy 1.1

Francesc Alted falted@pytables....
Thu Jul 3 12:44:06 CDT 2008


A Thursday 03 July 2008, Charles R Harris escrigué:
> On Thu, Jul 3, 2008 at 10:27 AM, Francesc Alted <falted@pytables.org> 
wrote:
> > A Thursday 03 July 2008, Charles R Harris escrigué:
> > > On Thu, Jul 3, 2008 at 2:00 AM, Francesc Alted
> > > <falted@pytables.org>
> >
> > wrote:
> > > > A Wednesday 02 July 2008, Charles R Harris escrigué:
> > > > > On Wed, Jul 2, 2008 at 9:58 AM, Charles R Harris
> > > > > <charlesr.harris@gmail.com>
> > > > >
> > > > > wrote:
> > > > > > On Wed, Jul 2, 2008 at 7:12 AM, Francesc Alted
> > > > > > <falted@pytables.org>
> > > > > >
> > > > > > wrote:
> > > > > >> Hi,
> > > > > >>
> > > > > >> I've seen that NumPy has changed the representation of
> > > > > >> complex numbers
> > > > > >>
> > > > > >> starting with NumPy 1.1.  Before, it was:
> > > > > >> >>> numpy.__version__
> > > > > >>
> > > > > >> '1.0.3'
> > > > > >>
> > > > > >> >>> repr(numpy.complex(0))    # The Python type
> > > > > >>
> > > > > >> '0j'
> > > > > >>
> > > > > >> >>> repr(numpy.complex128(0))  # The NumPy type
> > > > > >>
> > > > > >> '0j'
> > > > > >>
> > > > > >> Now, it is:
> > > > > >> >>> numpy.__version__
> > > > > >>
> > > > > >> '1.2.0.dev5313'
> > > > > >>
> > > > > >> >>> repr(numpy.complex(0))
> > > > > >>
> > > > > >> '0j'
> > > > > >>
> > > > > >> >>> repr(numpy.complex128(0))
> > > > > >>
> > > > > >> '(0.0+0.0j)'
> > > > > >>
> > > > > >> Not that I don't like the new way, but that broke a couple
> > > > > >> of tests of the PyTables suite, and before fixing it, I'd
> > > > > >> like to know if the new way would stay.  Also, I'm not
> > > > > >> certain why you have chosen a different representation
> > > > > >> than the Python type.
> > > > > >
> > > > > > Looks like different functions are being called, as
> > > > > > identical code is available for all the complex types.
> > > > > > Hmm... probably float is promoted to double and for double
> > > > > > the python repr is called. Since python can't handle
> > > > > > longdoubles the following code is called.
> > > > > >
> > > > > > static PyObject *
> > > > > > c@name@type_@kind@(PyObject *self)
> > > > > > {
> > > > > >     static char buf1[100];
> > > > > >     static char buf2[100];
> > > > > >     static char buf3[202];
> > > > > >     c@name@ x;
> > > > > >     x = ((PyC@Name@ScalarObject *)self)->obval;
> > > > > >     format_@name@(buf1, sizeof(buf1), x.real,
> > > > > > @NAME@PREC_@KIND@); format_@name@(buf2, sizeof(buf2),
> > > > > > x.imag, @NAME@PREC_@KIND@);
> > > > > >
> > > > > >     snprintf(buf3, sizeof(buf3), "(%s+%sj)", buf1, buf2);
> > > > > >     return PyString_FromString(buf3);
> > > > > > }
> > > > > >
> > > > > > So this can be fixed two ways, changing the cfloat and
> > > > > > cdouble types to call the above, or fixing the above to
> > > > > > look like python. Whichever way is chosen, I would rather
> > > > > > they go through the same generated functions as it keeps
> > > > > > the code paths simpler, puts the format choice in a single
> > > > > > location, and separates numpy from whatever might happen in
> > > > > > python.
> > > > >
> > > > > And I suspect this might be fallout from changeset #5014: Fix
> > > > > missing format code so longdoubles print with proper
> > > > > precision. The clongdouble repr function used to be missing
> > > > > and probably defaulted to cdouble.
> > > >
> > > > I'm not sure I follow you.  Are you telling that this is a
> > > > result of upcasting cfloats and cdoubles to clongdoubles when
> > > > representing NumPy complex numbers?  If so, why this should
> > > > happen at all?
> > >
> > > No, just that clongdoubles didn't use to print with sufficient
> > > repr precision for reasons I didn't understand, I added a NPY_
> > > prefix to the name of the generated printing function, and voila,
> > > it worked. For some reason cfloat and cdouble are using other
> > > print functions, which I suspect call the python repr function.
> > > Anyway, I propose the following
> > >
> > > 1) Make all the prints go through the same generated code. This
> > > makes the appearence consistent.
> >
> > +1
> >
> > > 2) Decide how the output should be formatted.
> > >
> > > So I can make either (0.0+0.0j) or perhaps something shorter the
> > > standard format. If you have a preference, speak up.
> >
> > My personal preference goes for following the python standard (I
> > suppose that this would be good for tests that check for the
> > representation). If this is difficult, the (0.0+0.0j)
> > representation will do.
>
> I'll add that is not possible to guarantee reproducible results for
> repr, especially for longdoubles, because of hardware differences
> and/or compilers. MSVC, for instance, defines long doubles to be the
> same as doubles while PPC uses some odd pairing of doubles. One
> reason str uses less precision is to hide those differences.

Ok.  But str also represents differently the 0j:

In [24]: str(numpy.complex64(0))
Out[24]: '(0.0+0.0j)'

In [25]: str(numpy.complex(0))
Out[25]: '0j'

In addition, I find the new representation not too nice looking:

In [35]: str(numpy.complex128(5/3j))
Out[35]: '(0.0+-1.66666666667j)'   # note the '+-' thing

In [36]: str(numpy.complex(5/3j))
Out[36]: '-1.66666666667j'


So, perhaps it would be a wise thing to mimic the python behaviour for 
this sort of things, if possible.

Cheers,

-- 
Francesc Alted


More information about the Numpy-discussion mailing list