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]]
Mateen Ulhaq
22.2k16 gold badges86 silver badges127 bronze badges
answered Jan 25, 2009 at 12:26
Andrew JaffeAndrew Jaffe
25.8k4 gold badges49 silver badges59 bronze badges
8
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]
Mateen Ulhaq
22.2k16 gold badges86 silver badges127 bronze badges
answered Jan 25, 2009 at 10:35
8
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']
>>>
answered Jan 25, 2009 at 11:57
gimelgimel
80.2k10 gold badges74 silver badges104 bronze badges
6
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.
answered Jan 25, 2009 at 10:32
cmsjrcmsjr
54.4k10 gold badges70 silver badges62 bronze badges
3
NumPy is a bit overkill, I think.
[p/10 for p in range[0, 10]]
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
Generally speaking, to do a step-by-1/x
up to y
you would do
x=100
y=2
[p/x for p in range[0, int[x*y]]]
[0.0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99]
[1/x
produced less rounding noise when I tested].
answered Feb 28, 2017 at 2:06
KalleKalle
13.1k7 gold badges61 silver badges76 bronze badges
1
scipy
has a built in function arange
which generalizes Python's
range[]
constructor to satisfy your requirement of float handling.
from scipy import arange
answered May 17, 2014 at 20:50
1
Similar to R's seq
function, this one returns a sequence in any order given the correct step value. The last value is equal to the stop value.
def seq[start, stop, step=1]:
n = int[round[[stop - start]/float[step]]]
if n > 1:
return[[start + step*i for i in range[n+1]]]
elif n == 1:
return[[start]]
else:
return[[]]
Results
seq[1, 5, 0.5]
[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
seq[10, 0, -1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
seq[10, 0, -2]
[10, 8, 6, 4, 2, 0]
seq[1, 1]
[ 1 ]
answered Jun 11, 2012 at 19:10
zeferinozeferino
5001 gold badge5 silver badges11 bronze badges
4
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']
self.start, self.stop, self.num = start, stop, num
self.step = [stop-start]/[num-1]
def __len__[self]:
return self.num
def __getitem__[self, i]:
if isinstance[i, slice]:
return [self[x] for x in range[*i.indices[len[self]]]]
if i < 0:
i = self.num + i
if i >= self.num:
raise IndexError['linspace object index out of range']
if i == self.num-1:
return self.stop
return self.start + i*self.step
def __repr__[self]:
return '{}[{}, {}, {}]'.format[type[self].__name__,
self.start, self.stop, self.num]
def __eq__[self, other]:
if not isinstance[other, linspace]:
return False
return [[self.start, self.stop, self.num] ==
[other.start, other.stop, other.num]]
def __ne__[self, other]:
return not self==other
def __hash__[self]:
return hash[[type[self], self.start, self.stop, self.num]]
answered Mar 2, 2018 at 15:00
Chris_RandsChris_Rands
36.4k12 gold badges79 silver badges110 bronze badges
This is my solution to get ranges with float steps.
Using this function it's not necessary to import numpy, nor install it.
I'm pretty sure that it could be improved and optimized. Feel free to do it and post it here.
from __future__ import division
from math import log
def xfrange[start, stop, step]:
old_start = start #backup this value
digits = int[round[log[10000, 10]]]+1 #get number of digits
magnitude = 10**digits
stop = int[magnitude * stop] #convert from
step = int[magnitude * step] #0.1 to 10 [e.g.]
if start == 0:
start = 10**[digits-1]
else:
start = 10**[digits]*start
data = [] #create array
#calc number of iterations
end_loop = int[[stop-start]//step]
if old_start == 0:
end_loop += 1
acc = start
for i in xrange[0, end_loop]:
data.append[acc/magnitude]
acc += step
return data
print xfrange[1, 2.1, 0.1]
print xfrange[0, 1.1, 0.1]
print xfrange[-1, 0.1, 0.1]
The output is:
[1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1]
[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0]
answered Dec 12, 2013 at 17:04
Carlos VegaCarlos Vega
1,3312 gold badges13 silver badges35 bronze badges
3
For completeness of boutique, a functional solution:
def frange[a,b,s]:
return [] if s > 0 and a > b or s < 0 and a < b or s==0 else [a]+frange[a+s,b,s]
answered Sep 12, 2015 at 7:44
Bijou TrouvailleBijou Trouvaille
8,1264 gold badges38 silver badges42 bronze badges
You can use this function:
def frange[start,end,step]:
return map[lambda x: x*step, range[int[start*1./step],int[end*1./step]]]
answered Jun 25, 2010 at 17:57
3
It can be done using Numpy library. arange[] function allows steps in float. But, it returns a numpy array which can be converted to list using tolist[] for our convenience.
for i in np.arange[0, 1, 0.1].tolist[]:
print i
answered Feb 12, 2016 at 12:18
start and stop are inclusive rather than one or the other [usually stop is excluded] and without imports, and using generators
def rangef[start, stop, step, fround=5]:
"""
Yields sequence of numbers from start [inclusive] to stop [inclusive]
by step [increment] with rounding set to n digits.
:param start: start of sequence
:param stop: end of sequence
:param step: int or float increment [e.g. 1 or 0.001]
:param fround: float rounding, n decimal places
:return:
"""
try:
i = 0
while stop >= start and step > 0:
if i==0:
yield start
elif start >= stop:
yield stop
elif start < stop:
if start == 0:
yield 0
if start != 0:
yield start
i += 1
start += step
start = round[start, fround]
else:
pass
except TypeError as e:
yield "type-error[{}]".format[e]
else:
pass
# passing
print[list[rangef[-100.0,10.0,1]]]
print[list[rangef[-100,0,0.5]]]
print[list[rangef[-1,1,0.2]]]
print[list[rangef[-1,1,0.1]]]
print[list[rangef[-1,1,0.05]]]
print[list[rangef[-1,1,0.02]]]
print[list[rangef[-1,1,0.01]]]
print[list[rangef[-1,1,0.005]]]
# failing: type-error:
print[list[rangef["1","10","1"]]]
print[list[rangef[1,10,"1"]]]
Python 3.6.2 [v3.6.2:5fd33b5, Jul 8 2017, 04:57:36] [MSC v.1900 64 bit [AMD64]]
answered Mar 1, 2018 at 21:53
Goran B.Goran B.
5344 silver badges13 bronze badges
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.
answered Jan 2, 2019 at 21:07
1
To counter the float precision issues, you could use the Decimal
module.
This demands an extra effort of converting to Decimal
from int
or float
while writing the code, but you can instead pass str
and modify the function if that sort
of convenience is indeed necessary.
from decimal import Decimal
def decimal_range[*args]:
zero, one = Decimal['0'], Decimal['1']
if len[args] == 1:
start, stop, step = zero, args[0], one
elif len[args] == 2:
start, stop, step = args + [one,]
elif len[args] == 3:
start, stop, step = args
else:
raise ValueError['Expected 1 or 2 arguments, got %s' % len[args]]
if not all[[type[arg] == Decimal for arg in [start, stop, step]]]:
raise ValueError['Arguments must be passed as ']
# neglect bad cases
if [start == stop] or [start > stop and step >= zero] or \
[start < stop and step start]^[step