20

I am using the arange function to define my for loop iterations and getting unexpected results.

i = arange(7.8, 8.4, 0.05)
print i

yeilds the following:

[ 7.8   7.85  7.9   7.95  8.    8.05  8.1   8.15  8.2   8.25  8.3   8.35 8.4 ]

yet using the stop value of 8.35 as follows

i = arange(7.8, 8.35, 0.05)

yields the following

[ 7.8   7.85  7.9   7.95  8.    8.05  8.1   8.15  8.2   8.25  8.3 ]

But I want my range to end at 8.35! I know I can use the stop value of > 8.35 and < 8.4 to achieve my result, but why is it different and in my mind, inconsistent?

Edit: I am using Python 2.7

wjandrea
  • 28,235
  • 9
  • 60
  • 81
smashtastic
  • 323
  • 1
  • 3
  • 11

5 Answers5

26

I'm guessing that you're seeing the effects of floating point rounding.

numpy.arange does the same thing as python's range: It doesn't include the "endpoint". (e.g. range(0, 4, 2) will yield [0,2] instead of [0,2,4])

However, for floating point steps, the rounding errors are accumulate, and occasionally the last value will actually include the endpoint.

As noted in the documentation for arange:

When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases.

numpy.linspace generates a specified number of points between a starting and ending point. Incidentally, it does include the endpoints by default.

Joe Kington
  • 275,208
  • 71
  • 604
  • 463
  • hi - I have tried to find the help docs online for linspace but it is strangely blank ... do you have a suitable link? – smashtastic Apr 04 '12 at 14:59
  • 1
    http://docs.scipy.org/doc/numpy-1.6.0/reference/generated/numpy.linspace.html Alternately, you can just do `pydoc numpy.linspace` or if you're in a python interpreter `help(numpy.linspace)`, or in `ipython`, it's just `numpy.linspace?`. – Joe Kington Apr 04 '12 at 15:13
  • that's clear in the docs, but it doesn't make numpy.arange useful does it? – marbel Jun 02 '17 at 15:50
14

Perhaps it has to do with limitations on floating point numbers. Due to machine precision, it is not possible to store every conceivable value perfectly as a floating point. For example:

>>> 8.4
8.4000000000000004
>>> 8.35
8.3499999999999996

So, 8.4 as a floating point is slightly greater than the actual value of 8.4, while 8.35 as a floating point is a tiny bit less.

Brendan Wood
  • 6,220
  • 3
  • 30
  • 28
  • 1
    this seems to output with python 2.6 but with 2.7, >>>8.4 comes out as 8.4 – avasal Apr 04 '12 at 13:02
  • 4
    So it does, that's interesting. It looks like they've changed how floats are printed, though the underlying number of still the same (slightly incorrect), which you can see by comparing the hex values for the floats in Python 2.6 and 2.7. – Brendan Wood Apr 04 '12 at 13:10
  • `numpy.arange` isn't able to handle this correctly in Python 2.7 either. – marbel Jun 02 '17 at 15:50
  • Same problem in Python 3.7.3 glad to know I'm not the only one. – Elke May 11 '20 at 06:17
5

the help of arange function says

    For floating point arguments, the length of the result is
    ``ceil((stop - start)/step)``.  Because of floating point overflow,
    this rule may result in the last element of `out` being greater
    than `stop`.

for python 2.7, Conversions between floating-point numbers and strings are now correctly rounded on most platforms.

in 2.7

>>> float(repr(2.3))
2.3

in 2.6

>>> float(repr(2.3))
2.2999999999999998
avasal
  • 14,350
  • 4
  • 31
  • 47
2

I had the same problem and I implemented my own function to correct for this rounding issue with numpy.arange :

import numpy as np
def my_arange(a, b, dr, decimals=6):
    res = [a]
    k = 1
    while res[-1] < b:
        tmp = round(a + k*dr,decimals)
        if tmp > b:
            break   
        res.append(tmp)
        k+=1

    return np.asarray(res)
marbel
  • 7,560
  • 6
  • 49
  • 68
ClaGor
  • 21
  • 1
1

Add a bit of a corrector float to the endpoint:

import numpy as np

step = 0.05
corr = 0.01 if step == 0.05 else 0.0

i = np.arange(7.8,8.35+corr,step)
print(i)

OUTPUT:

$ python a.py [7.8 7.85 7.9 7.95 8. 8.05 8.1 8.15 8.2 8.25 8.3 8.35]

Falcon77
  • 31
  • 2