How do I use a decimal step value for range()?

Viewed 1066772

How do I iterate between 0 and 1 by a step of 0.1?

This says that the step argument cannot be zero:

for i in range(0, 1, 0.1):
    print(i)
34 Answers

Rather than using a decimal step directly, it's much safer to express this in terms of how many points you want. Otherwise, floating-point rounding error is likely to give you a wrong result.

Use the linspace function from the NumPy library (which isn't part of the standard library but is relatively easy to obtain). linspace takes a number of points to return, and also lets you specify whether or not to include the right endpoint:

>>> np.linspace(0,1,11)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ])
>>> np.linspace(0,1,10,endpoint=False)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])

If you really want to use a floating-point step value, use numpy.arange:

>>> import numpy as np
>>> np.arange(0.0, 1.0, 0.1)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])

Floating-point rounding error will cause problems, though. Here's a simple case where rounding error causes arange to produce a length-4 array when it should only produce 3 numbers:

>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])

range() can only do integers, not floating point.

Use a list comprehension instead to obtain a list of steps:

[x * 0.1 for x in range(0, 10)]

More generally, a generator comprehension minimizes memory allocations:

xs = (x * 0.1 for x in range(0, 10))
for x in xs:
    print(x)

Building on 'xrange([start], stop[, step])', you can define a generator that accepts and produces any type you choose (stick to types supporting + and <):

>>> def drange(start, stop, step):
...     r = start
...     while r < stop:
...         yield r
...         r += step
...         
>>> i0=drange(0.0, 1.0, 0.1)
>>> ["%g" % x for x in i0]
['0', '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']
>>> 

Increase the magnitude of i for the loop and then reduce it when you need it.

for i * 100 in range(0, 100, 10):
    print i / 100.0

EDIT: I honestly cannot remember why I thought that would work syntactically

for i in range(0, 11, 1):
    print i / 10.0

That should have the desired output.

The range() built-in function returns a sequence of integer values, I'm afraid, so you can't use it to do a decimal step.

I'd say just use a while loop:

i = 0.0
while i <= 1.0:
    print i
    i += 0.1

If you're curious, Python is converting your 0.1 to 0, which is why it's telling you the argument can't be zero.

And if you do this often, you might want to save the generated list r

r=map(lambda x: x/10.0,range(0,10))
for i in r:
    print i

more_itertools is a third-party library that implements a numeric_range tool:

import more_itertools as mit


for x in mit.numeric_range(0, 1, 0.1):
    print("{:.1f}".format(x))

Output

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

This tool also works for Decimal and Fraction.

Lots of the solutions here still had floating point errors in Python 3.6 and didnt do exactly what I personally needed.

Function below takes integers or floats, doesnt require imports and doesnt return floating point errors.

def frange(x, y, step):
    if int(x + y + step) == (x + y + step):
        r = list(range(int(x), int(y), int(step)))
    else:
        f = 10 ** (len(str(step)) - str(step).find('.') - 1)
        rf = list(range(int(x * f), int(y * f), int(step * f)))
        r = [i / f for i in rf]

    return r

I know I'm late to the party here, but here's a trivial generator solution that's working in 3.6:

def floatRange(*args):
    start, step = 0, 1
    if len(args) == 1:
        stop = args[0]
    elif len(args) == 2:
        start, stop = args[0], args[1]
    elif len(args) == 3:
        start, stop, step = args[0], args[1], args[2]
    else:
        raise TypeError("floatRange accepts 1, 2, or 3 arguments. ({0} given)".format(len(args)))
    for num in start, step, stop:
        if not isinstance(num, (int, float)):
            raise TypeError("floatRange only accepts float and integer arguments. ({0} : {1} given)".format(type(num), str(num)))
    for x in range(int((stop-start)/step)):
        yield start + (x * step)
    return

then you can call it just like the original range()... there's no error handling, but let me know if there is an error that can be reasonably caught, and I'll update. or you can update it. this is StackOverflow.

This one liner will not clutter your code. The sign of the step parameter is important.

def frange(start, stop, step):
    return [x*step+start for x in range(0,round(abs((stop-start)/step)+0.5001),
        int((stop-start)/step<0)*-2+1)]
sign = lambda x: (1, -1)[x < 0]
def frange(start, stop, step):
    i = 0
    r=len(str(step).split('.')[-1])
    args=(start,stop,step)
    if not step :return []
    if all(int(i)==float(i) for i in args):
        start,stop,step=map(int,args)
    if sign(step)==1:
        while start + i * step < stop:
            yield round(start + i * step,r)
            i += 1
    else:
        while start + i * step > stop:
            yield round(start + i * step,r)
            i += 1
f = lambda x,y,z: (x+i*z for i in range(int((y-x)/z)))

Above can do fractional step without resorting to any library.

Related