[Python-ideas] Floating point contexts in Python core

Sturla Molden sturla at molden.no
Thu Oct 11 14:31:31 CEST 2012


On 11.10.2012 13:35, Steven D'Aprano wrote:

> I would gladly give up a small amount of speed for better control
> over floats, such as whether 1/0.0 raised an exception or
> returned infinity.
>
> If I wanted fast code, I'd be using C. I'm happy with *fast enough*.
>
> For example, 1/0.0 in a continued fraction is generally harmless,
> provided it returns infinity. If it raises an exception, you have
> to write slow, ugly code to evaluate continued fractions robustly.
> I wouldn't expect 1/0.0 -> infinity to becomes the default, but
> I'd like a runtime switch to turn it on and off as needed.


For those who use Python for numerical or scientific computing or 
computer graphics this is a real issue.

First: The standard way of dealing with 1/0.0 in this context, since the 
days of FORTRAN, is to return an inf. Consequently, that is what NumPy 
does, as does Matlab, R and most C programs and libraries.

Now compare:

 >>> 1.0/0.0

Traceback (most recent call last):
   File "<pyshell#0>", line 1, in <module>
     1.0/0.0
ZeroDivisionError: float division by zero

With this:

 >>> import numpy as np
 >>> np.float64(1.0)/np.float64(0.0)
inf


Thus, the NumPy float64 scalar behaves differently from the Python float 
scalar!

In less than trivial expressions, we can have a combination of Python 
floats and ints and NumPy types (arrays or scalars). What this means is 
that the behavior is undefined. You might get an inf, or you might get 
an exception. Who can tell?

The issue also affects integers:


 >>> 1/0

Traceback (most recent call last):
   File "<pyshell#5>", line 1, in <module>
     1/0
ZeroDivisionError: integer division or modulo by zero


whereas:

 >>> np.int64(1)/np.int64(0)
0

 >>> np.int32(1)/np.int32(0)
0


And with arrays:

 >>> np.ones(10, dtype=np.int)/np.zeros(10, dtype=np.int)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

 >>> np.ones(10, dtype=np.float64)/np.zeros(10, dtype=np.float64)
array([ inf,  inf,  inf,  inf,  inf,  inf,  inf,  inf,  inf,  inf])


I think for the sake of us who actually need computation -- believe it 
or not, Python is rapidly becoming the language of choice for numerical 
computing -- it would be very nice is this was controllable. Not just 
the behavior of floats, but also the behavior of ints.

A global switch in the sys module would make life a lot easier. Even 
better would be a context manager that allows us to set up a "numerical" 
context for local expressions using a with statement. That would not 
have a lasting effect, but just affect the context. Preferably it should 
not even propagate across function calls. Something like this:


def foobar():
     1/0.0 # raise an exception
     1/0   # raise an exception
	
with sys.numerical:
     1/0.0 # return inf
     1/0   # return 0
     foobar()


(NumPy actually prints divide by zero warnings on their first 
occurrence, but I removed it for clarity.)



Sturla Molden


















More information about the Python-ideas mailing list