'numpy arange: how to make "precise" array of floats?

In short, the problem I encounter is this:

aa = np.arange(-1., 0.001, 0.01)
aa[-1]
Out[16]: 8.8817841970012523e-16

In reality, this cause a series problem since my simulations doesn't allow positive value inputs.

I can sort of get around by doing:

aa = np.arange(-100, 1, 1)/100.
aa[-1]
Out[21]: 0.0

But this is a pain. Practically you can't do this every time.

This seems like such a basic problem. There's gotta be something I am missing here. By the way, I am using Python 2.7.13.



Solution 1:[1]

This happens because Python (like most modern programming languages) uses floating point arithmetic, which cannot exactly represent some numbers (see Is floating point math broken?).

This means that, regardless of whether you're using Python 2, Python 3, R, C, Java, etc. you have to think about the effects of adding two floating point numbers together.

np.arange works by repeatedly adding the step value to the start value, and this leads to imprecision in the end:

>>> start = -1
>>> for i in range(1000):
...    start += 0.001
>>> start
8.81239525796218e-16

Similarly:

>>> x = np.arange(-1., 0.001, 0.01)
>>> x[-1]
8.8817841970012523e-16

The typical pattern used to circumvent this is to work with integers whenever possible if repeated operations are needed. So, for example, I would do something like this:

>>> x = 0.01 * np.arange(-100, 0.1)
>>> x[-1]
0.0

Alternatively, you could create a one-line convenience function that will do this for you:

>>> def safe_arange(start, stop, step):
...    return step * np.arange(start / step, stop / step)

>>> x = safe_arange(-1, 0.001, 0.01)
>>> x[-1]
0

But note that even this can't get around the limits of floating point precision; for example, the number -0.99 cannot be represented exactly in floating point:

>>> val = -0.99
>>> print('{0:.20f}'.format(val))
-0.98999999999999999112

So you must always keep that in mind when working with floating point numbers, in any language.

Solution 2:[2]

Using np.linespace solved it for me:

For example np.linspace(0.5, 0.9, 5) produce [0.5 0.6 0.7 0.8 0.9].

Solution 3:[3]

We don't get to forget about the limitations of floating-point arithmetics. Repeatedly adding 0.01, or rather the double-precision float that is close to 0.01, will result in the kind of effects you observe.

To ensure that an array does not contain positive numbers, use numpy.clip:

aa = np.clip(np.arange(-1., 0.001, 0.01), None, 0)

Solution 4:[4]

I have the same problem as you do.

Here is a simple solution:

for b in np.arange(maxb,minb+stepb,stepb):    
    for a in np.arange(mina,maxa+stepa,stepa):        
        a=round(a,2);b=round(b,2); # 2 is the size of the floating part.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Edo Wexler
Solution 3
Solution 4 Issam Chekakta