3

When I run:

import numpy as np
np.arange(14.1,15.1,0.1)

I get:

array([14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7, 14.8, 14.9, 15. ])

yet when I run:

np.arange(15.1,16.1,0.1)

I get:

array([15.1, 15.2, 15.3, 15.4, 15.5, 15.6, 15.7, 15.8, 15.9, 16. , 16.1])

What is up with the missing 15.1? Why does the output in one case show the final number and in the other case not?

I have read the documentation on numpy-arange. It states that the "interval does not include stop value, except in some cases where step is not an integer and floating point round-off affects the length of out."

My question is: How to get the code to behave consistent?

I am iterating over a list of paired numbers (e.g. 4 and 15, or 44.2 and 46.4) and for each pair, I want to create a list with steps of 0.1 between the pairs (e.g 4 and 4.5 would be: 4.1, 4.2, 4.3, 4.4, 4.5). But it is important that the code behaves consistent.

yatu
  • 86,083
  • 12
  • 84
  • 139
J.A.Cado
  • 715
  • 5
  • 13
  • 24
  • 4
    From the doc: `When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use numpy.linspace for these cases.` – Gianluca Micchi Jun 05 '20 at 14:09

1 Answers1

7

This is due to floating point precision. Taking a look at the actual decimal representation of the floats without any rounding will make it clearer:

15.1%1
# 0.09999999999999964

16.1%1
# 0.10000000000000142

Note that just like python's range, np.arange does not include the end in the created range, hence unless the floating point error results in a value greater than the step in the end of the range, it will not be included. That is the case of the first float, which has a decimal part lower than 0.1.

The docs do suggest using np.linspace when using a non-integer step, since the results can be inconsistent, precisely for the above reason. Moreover it includes a endpoint parameter, which allows you to do:

np.linspace(14.1,15.1, 11, endpoint=True)
# array([14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7, 14.8, 14.9, 15. , 15.1])

And, as @divakar suggests, to generalize you can set num as:

start = 14.1
stop = 15.1
step = 0.1
num = 1+int((stop-start)/step)

np.linspace(start, stop, num, endpoint=True)
# array([14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7, 14.8, 14.9, 15. , 15.1])
yatu
  • 86,083
  • 12
  • 84
  • 139
  • Thanks! That explains my issue, now how to fix it? – J.A.Cado Jun 05 '20 at 14:10
  • 1
    Use `np.linspace` as already mentioned in the comments. The difference is that you'll have to specify beforehand the amount of samples to generate @J.A.Cado – yatu Jun 05 '20 at 14:20
  • 1
    @yatu Good insight with `%`. Think you can create a custom one with `1+int((stop-start)/step)` as `num` to have `step` as an input arg to linspace. – Divakar Jun 05 '20 at 14:23
  • Thanks @divakar much nicer with a general approach – yatu Jun 05 '20 at 14:29