[PYTHON MATRIX-SIG] Type Coercion

James Hugunin jjh@Goldilocks.LCS.MIT.EDU
Wed, 7 Feb 96 15:18:00 EST


I still don't see a great solution to this one.  

Here's my attempt to boil the issue down to its essence:
(While I only talk about floats and doubles here, this same issue
comes up with longs and shorts and bytes as well as float and double
complex numbers).



C (and FORTRAN) has two kinds of floating point numbers, float and
double.  A double has more precision and range than a float.

In C if I multiply a double by a float I get a double.

Python has only one floating point type, call this a PyFloat.
Internally, these numbers store their data using C doubles.  However,
the standard C interface to PyFloats allows them to be extracted into
either a C float or a C double (PyArg_ParseTuple), so its not clear
that a PyFloat should really be considered either a float or a double.
Nevertheless, its obvious that PyFloats are more closely related to
doubles than floats.

An array can be of type float or double.

It is reasonable to have the result of multiplying an array of floats
by an array of doubles be an array of doubles.

What should happen if I multiply an array of floats by a PyFloat?

Three things can happen:

1) Return an array of floats (possible undesired loss of precision)
2) Return an array of doubles (possible undesired gain of precision)
3) Raise an exception (very annoying to the user)

Currently there is also the convention that when an array of floats or
doubles returns a single element, that element will be a PyFloat.
Perhaps this behavior should be altered as the following examples of
confusing behavior using either approach 1 or approach 2 show.

a_float and a_double are 1d arrays of the appropriate type.

Using Method 1)

a_float[0]*a_float -> a_float (good)
a_double[0]*a_float -> a_float (not good)

Using Method 2)
a_float[0]*a_float -> a_double (not good)
a_double[0]*a_float -> a_double (good)

This particular problem could be eliminated by having a_float[0]
return a 0d array of floats with a single element.  Perhaps this is
worth considering.


The next confusing behavior involves python numeric constants.
Again, considering the two non-exception producing methods discussed
above:

very_precise_constant = 1.2345678910

Using method 1)
0.5*a_float -> a_float (good)
very_precise_constant*a_float -> a_float (probably not good)

Using method 2)
0.5*a_float -> a_double (not good)
very_precise_constant*a_float -> a_double (probably good)

Using this method, the only way to write 0.5*a_float and get an array
of floats back is to write:

array(0.5, Float(32))*a_float

This becomes very ugly for any reasonably sized expression.

Currently, method 3 is used because it seems to make everybody
unhappy, yet it doesn't do anything terribly "wrong".


Personally I would prefer to use method 1, combined with the major
change to the existing system that scalars are returned as 0d arrays,
not as python scalars.

This means that python scalars will be considered to have a malleable
type (as they in fact seem to) and can be cast to either a float or a
double as needed in the particular equation.  If you have a
high-precision number whose precision you need to hold on to, you will
need to say:

very_precise_constant = array(1.2345678910, Float(64))

As I said at the beginning, none of these three options really appeals
to me, yet I feel that this is a very important issue that really
needs to be resolved before the BETA release.

Opinions? (like I have to ask on this issue) - Jim

=================
MATRIX-SIG  - SIG on Matrix Math for Python

send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================