## 问题：浮点数的range（）

`range()`Python中的浮点数是否等效？

``````>>> range(0.5,5,1.5)
[0, 1, 2, 3, 4]
>>> range(0.5,5,0.5)

Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
range(0.5,5,0.5)
ValueError: range() step argument must not be zero``````

Is there a `range()` equivalent for floats in Python?

``````>>> range(0.5,5,1.5)
[0, 1, 2, 3, 4]
>>> range(0.5,5,0.5)

Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
range(0.5,5,0.5)
ValueError: range() step argument must not be zero
``````

## 回答 0

``````def frange(x, y, jump):
while x < y:
yield x
x += jump``````

``````>>> list(frange(0, 100, 0.1))[-1]
99.9999999999986``````

``````>>> import decimal
>>> list(frange(0, 100, decimal.Decimal('0.1')))[-1]
Decimal('99.9')``````

``````import decimal

def drange(x, y, jump):
while x < y:
yield float(x)
x += decimal.Decimal(jump)``````

``````>>> list(drange(0, 100, '0.1'))[-1]
99.9``````

I don’t know a built-in function, but writing one like this shouldn’t be too complicated.

``````def frange(x, y, jump):
while x < y:
yield x
x += jump
``````

As the comments mention, this could produce unpredictable results like:

``````>>> list(frange(0, 100, 0.1))[-1]
99.9999999999986
``````

To get the expected result, you can use one of the other answers in this question, or as @Tadhg mentioned, you can use `decimal.Decimal` as the `jump` argument. Make sure to initialize it with a string rather than a float.

``````>>> import decimal
>>> list(frange(0, 100, decimal.Decimal('0.1')))[-1]
Decimal('99.9')
``````

Or even:

``````import decimal

def drange(x, y, jump):
while x < y:
yield float(x)
x += decimal.Decimal(jump)
``````

And then:

``````>>> list(drange(0, 100, '0.1'))[-1]
99.9
``````

## 回答 1

``[x / 10.0 for x in range(5, 50, 15)]``

``map(lambda x: x/10.0, range(5, 50, 15))``

You can either use:

``````[x / 10.0 for x in range(5, 50, 15)]
``````

or use lambda / map:

``````map(lambda x: x/10.0, range(5, 50, 15))
``````

## 回答 2

``````>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([  0.        ,   3.33333333,   6.66666667,  10.        ])``````

I used to use `numpy.arange` but had some complications controlling the number of elements it returns, due to floating point errors. So now I use `linspace`, e.g.:

``````>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([  0.        ,   3.33333333,   6.66666667,  10.        ])
``````

## 回答 3

Pylab具有`frange`（实际上是的包装器`matplotlib.mlab.frange`）：

``````>>> import pylab as pl
>>> pl.frange(0.5,5,0.5)
array([ 0.5,  1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5,  5. ])``````

Pylab has `frange` (a wrapper, actually, for `matplotlib.mlab.frange`):

``````>>> import pylab as pl
>>> pl.frange(0.5,5,0.5)
array([ 0.5,  1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5,  5. ])
``````

## 回答 4

``[x * .5 for x in range(10)]``

``itertools.imap(lambda x: x * .5, xrange(10)) # or range(10) as appropriate``

``````itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10)
# without applying the `islice`, we get an infinite stream of half-integers.``````

Eagerly evaluated (2.x `range`):

``````[x * .5 for x in range(10)]
``````

Lazily evaluated (2.x `xrange`, 3.x `range`):

``````itertools.imap(lambda x: x * .5, xrange(10)) # or range(10) as appropriate
``````

Alternately:

``````itertools.islice(itertools.imap(lambda x: x * .5, itertools.count()), 10)
# without applying the `islice`, we get an infinite stream of half-integers.
``````

## 回答 5

``````>>> from itertools import count, takewhile
>>> def frange(start, stop, step):
return takewhile(lambda x: x< stop, count(start, step))

>>> list(frange(0.5, 5, 1.5))
# [0.5, 2.0, 3.5]``````

using `itertools`: lazily evaluated floating point range:

``````>>> from itertools import count, takewhile
>>> def frange(start, stop, step):
return takewhile(lambda x: x< stop, count(start, step))

>>> list(frange(0.5, 5, 1.5))
# [0.5, 2.0, 3.5]
``````

## 回答 6

`more_itertools.numeric_range(start, stop, step)` 行为类似于内置函数范围，但可以处理浮点数，小数和小数类型。

``````>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)``````

I helped add the function numeric_range to the package more-itertools.

`more_itertools.numeric_range(start, stop, step)` acts like the built in function range but can handle floats, Decimal, and Fraction types.

``````>>> from more_itertools import numeric_range
>>> tuple(numeric_range(.1, 5, 1))
(0.1, 1.1, 2.1, 3.1, 4.1)
``````

## 回答 7

``````from fractions import Fraction

def frange(start, stop, jump, end=False, via_str=False):
"""
Equivalent of Python 3 range for decimal numbers.

Notice that, because of arithmetic errors, it is safest to
pass the arguments as strings, so they can be interpreted to exact fractions.

>>> assert Fraction('1.1') - Fraction(11, 10) == 0.0
>>> assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)

Parameter `via_str` can be set to True to transform inputs in strings and then to fractions.
When inputs are all non-periodic (in base 10), even if decimal, this method is safe as long
as approximation happens beyond the decimal digits that Python uses for printing.

For example, in the case of 0.1, this is the case:

>>> assert str(0.1) == '0.1'
>>> assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'

If you are not sure whether your decimal inputs all have this property, you are better off
passing them as strings. String representations can be in integer, decimal, exponential or
even fraction notation.

>>> assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
>>> assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
>>> assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
>>> assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
>>> assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
>>> assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0

"""
if via_str:
start = str(start)
stop = str(stop)
jump = str(jump)
start = Fraction(start)
stop = Fraction(stop)
jump = Fraction(jump)
while start < stop:
yield float(start)
start += jump
if end and start == stop:
yield(float(start))``````

``````assert Fraction('1.1') - Fraction(11, 10) == 0.0
assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)

assert str(0.1) == '0.1'
assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'

assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0

assert list(frange(2, 3, '1/6', end=True))[-1] == 3.0
assert list(frange(0, 100, '1/3', end=True))[-1] == 100.0``````

GitHub上的代码

There is no such built-in function, but you can use the following (Python 3 code) to do the job as safe as Python allows you to.

``````from fractions import Fraction

def frange(start, stop, jump, end=False, via_str=False):
"""
Equivalent of Python 3 range for decimal numbers.

Notice that, because of arithmetic errors, it is safest to
pass the arguments as strings, so they can be interpreted to exact fractions.

>>> assert Fraction('1.1') - Fraction(11, 10) == 0.0
>>> assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)

Parameter `via_str` can be set to True to transform inputs in strings and then to fractions.
When inputs are all non-periodic (in base 10), even if decimal, this method is safe as long
as approximation happens beyond the decimal digits that Python uses for printing.

For example, in the case of 0.1, this is the case:

>>> assert str(0.1) == '0.1'
>>> assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'

If you are not sure whether your decimal inputs all have this property, you are better off
passing them as strings. String representations can be in integer, decimal, exponential or
even fraction notation.

>>> assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
>>> assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
>>> assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
>>> assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
>>> assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
>>> assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0

"""
if via_str:
start = str(start)
stop = str(stop)
jump = str(jump)
start = Fraction(start)
stop = Fraction(stop)
jump = Fraction(jump)
while start < stop:
yield float(start)
start += jump
if end and start == stop:
yield(float(start))
``````

You can verify all of it by running a few assertions:

``````assert Fraction('1.1') - Fraction(11, 10) == 0.0
assert Fraction( 0.1 ) - Fraction(1, 10) == Fraction(1, 180143985094819840)

assert str(0.1) == '0.1'
assert '%.50f' % 0.1 == '0.10000000000000000555111512312578270211815834045410'

assert list(frange(1, 100.0, '0.1', end=True))[-1] == 100.0
assert list(frange(1.0, '100', '1/10', end=True))[-1] == 100.0
assert list(frange('1', '100.0', '.1', end=True))[-1] == 100.0
assert list(frange('1.0', 100, '1e-1', end=True))[-1] == 100.0
assert list(frange(1, 100.0, 0.1, end=True))[-1] != 100.0
assert list(frange(1, 100.0, 0.1, end=True, via_str=True))[-1] == 100.0

assert list(frange(2, 3, '1/6', end=True))[-1] == 3.0
assert list(frange(0, 100, '1/3', end=True))[-1] == 100.0
``````

Code available on GitHub

## 回答 8

``````numpy.linspace(0, 10, 41)
array([  0.  ,   0.25,   0.5 ,   0.75,   1.  ,   1.25,   1.5 ,   1.75,
2.  ,   2.25,   2.5 ,   2.75,   3.  ,   3.25,   3.5 ,   3.75,
4.  ,   4.25,   4.5 ,   4.75,   5.  ,   5.25,   5.5 ,   5.75,
6.  ,   6.25,   6.5 ,   6.75,   7.  ,   7.25,   7.5 ,   7.75,
8.  ,   8.25,   8.5 ,   8.75,   9.  ,   9.25,   9.5 ,   9.75,  10.
])``````

``````# Float range function - string formatting method
def frange_S (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield float(("%0." + str(decimals) + "f") % (i * skip))``````

``````# Float range function - rounding method
def frange_R (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield round(i * skip, ndigits = decimals)``````

``````def compare_methods (start, stop, skip):

string_test  = frange_S(start, stop, skip)
round_test   = frange_R(start, stop, skip)

for s, r in zip(string_test, round_test):
print(s, r)

compare_methods(-2, 10, 1/3)``````

``````-2.0 -2.0
-1.67 -1.67
-1.33 -1.33
-1.0 -1.0
-0.67 -0.67
-0.33 -0.33
0.0 0.0
...
8.0 8.0
8.33 8.33
8.67 8.67
9.0 9.0
9.33 9.33
9.67 9.67``````

``````>>> import timeit

>>> setup = """
... def frange_s (start, stop, skip = 1.0, decimals = 2):
...     for i in range(int(start / skip), int(stop / skip)):
...         yield float(("%0." + str(decimals) + "f") % (i * skip))
... def frange_r (start, stop, skip = 1.0, decimals = 2):
...     for i in range(int(start / skip), int(stop / skip)):
...         yield round(i * skip, ndigits = decimals)
... start, stop, skip = -1, 8, 1/3
... """

>>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000))
0.024284090992296115

>>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000))
0.025324633985292166``````

``````# "Missing" the last value (10.0)
for x in frange_R(0, 10, 0.25):
print(x)

0.25
0.5
0.75
1.0
...
9.0
9.25
9.5
9.75``````

``````# Clearly we know that 10 - 9.43 is equal to 0.57
for x in frange_R(0, 10, 3/7):
print(x)

0.0
0.43
0.86
1.29
...
8.14
8.57
9.0
9.43``````

Why Is There No Floating Point Range Implementation In The Standard Library?

As made clear by all the posts here, there is no floating point version of `range()`. That said, the omission makes sense if we consider that the `range()` function is often used as an index (and of course, that means an accessor) generator. So, when we call `range(0,40)`, we’re in effect saying we want 40 values starting at 0, up to 40, but non-inclusive of 40 itself.

When we consider that index generation is as much about the number of indices as it is their values, the use of a float implementation of `range()` in the standard library makes less sense. For example, if we called the function `frange(0, 10, 0.25)`, we would expect both 0 and 10 to be included, but that would yield a vector with 41 values.

Thus, an `frange()` function depending on its use will always exhibit counter intuitive behavior; it either has too many values as perceived from the indexing perspective or is not inclusive of a number that reasonably should be returned from the mathematical perspective.

The Mathematical Use Case

With that said, as discussed, `numpy.linspace()` performs the generation with the mathematical perspective nicely:

``````numpy.linspace(0, 10, 41)
array([  0.  ,   0.25,   0.5 ,   0.75,   1.  ,   1.25,   1.5 ,   1.75,
2.  ,   2.25,   2.5 ,   2.75,   3.  ,   3.25,   3.5 ,   3.75,
4.  ,   4.25,   4.5 ,   4.75,   5.  ,   5.25,   5.5 ,   5.75,
6.  ,   6.25,   6.5 ,   6.75,   7.  ,   7.25,   7.5 ,   7.75,
8.  ,   8.25,   8.5 ,   8.75,   9.  ,   9.25,   9.5 ,   9.75,  10.
])
``````

The Indexing Use Case

And for the indexing perspective, I’ve written a slightly different approach with some tricksy string magic that allows us to specify the number of decimal places.

``````# Float range function - string formatting method
def frange_S (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield float(("%0." + str(decimals) + "f") % (i * skip))
``````

Similarly, we can also use the built-in `round` function and specify the number of decimals:

``````# Float range function - rounding method
def frange_R (start, stop, skip = 1.0, decimals = 2):
for i in range(int(start / skip), int(stop / skip)):
yield round(i * skip, ndigits = decimals)
``````

A Quick Comparison & Performance

Of course, given the above discussion, these functions have a fairly limited use case. Nonetheless, here’s a quick comparison:

``````def compare_methods (start, stop, skip):

string_test  = frange_S(start, stop, skip)
round_test   = frange_R(start, stop, skip)

for s, r in zip(string_test, round_test):
print(s, r)

compare_methods(-2, 10, 1/3)
``````

The results are identical for each:

``````-2.0 -2.0
-1.67 -1.67
-1.33 -1.33
-1.0 -1.0
-0.67 -0.67
-0.33 -0.33
0.0 0.0
...
8.0 8.0
8.33 8.33
8.67 8.67
9.0 9.0
9.33 9.33
9.67 9.67
``````

And some timings:

``````>>> import timeit

>>> setup = """
... def frange_s (start, stop, skip = 1.0, decimals = 2):
...     for i in range(int(start / skip), int(stop / skip)):
...         yield float(("%0." + str(decimals) + "f") % (i * skip))
... def frange_r (start, stop, skip = 1.0, decimals = 2):
...     for i in range(int(start / skip), int(stop / skip)):
...         yield round(i * skip, ndigits = decimals)
... start, stop, skip = -1, 8, 1/3
... """

>>> min(timeit.Timer('string_test = frange_s(start, stop, skip); [x for x in string_test]', setup=setup).repeat(30, 1000))
0.024284090992296115

>>> min(timeit.Timer('round_test = frange_r(start, stop, skip); [x for x in round_test]', setup=setup).repeat(30, 1000))
0.025324633985292166
``````

Looks like the string formatting method wins by a hair on my system.

The Limitations

And finally, a demonstration of the point from the discussion above and one last limitation:

``````# "Missing" the last value (10.0)
for x in frange_R(0, 10, 0.25):
print(x)

0.25
0.5
0.75
1.0
...
9.0
9.25
9.5
9.75
``````

Further, when the `skip` parameter is not divisible by the `stop` value, there can be a yawning gap given the latter issue:

``````# Clearly we know that 10 - 9.43 is equal to 0.57
for x in frange_R(0, 10, 3/7):
print(x)

0.0
0.43
0.86
1.29
...
8.14
8.57
9.0
9.43
``````

There are ways to address this issue, but at the end of the day, the best approach would probably be to just use Numpy.

## 回答 9

kichik提供了一个没有numpy等相关性的解决方案，但是由于浮点运算的原因，它的行为常常出乎意料。正如blubberdiblub所说，其他元素很容易潜入结果。例如，`naive_frange(0.0, 1.0, 0.1)`将产生`0.999...`作为其最后一个值，因此总共产生11个值。

``````def frange(x, y, jump=1.0):
'''Range for floats.'''
i = 0.0
x = float(x)  # Prevent yielding integers.
x0 = x
epsilon = jump / 2.0
yield x  # yield always first value
while x + epsilon < y:
i += 1.0
x = x0 + i * jump
yield x``````

``````> a = list(frange(0.0, 1.0, 0.1))
> a[-1]
0.9
> len(a)
10``````

``````> b = list(frange(0.0, 1000000.0, 0.1))
> b[-1]
999999.9
> len(b)
10000000``````

A solution without numpy etc dependencies was provided by kichik but due to the floating point arithmetics, it often behaves unexpectedly. As noted by me and blubberdiblub, additional elements easily sneak into the result. For example `naive_frange(0.0, 1.0, 0.1)` would yield `0.999...` as its last value and thus yield 11 values in total.

A robust version is provided here:

``````def frange(x, y, jump=1.0):
'''Range for floats.'''
i = 0.0
x = float(x)  # Prevent yielding integers.
x0 = x
epsilon = jump / 2.0
yield x  # yield always first value
while x + epsilon < y:
i += 1.0
x = x0 + i * jump
yield x
``````

Because the multiplication, the rounding errors do not accumulate. The use of `epsilon` takes care of possible rounding error of the multiplication, even though issues of course might rise in the very small and very large ends. Now, as expected:

``````> a = list(frange(0.0, 1.0, 0.1))
> a[-1]
0.9
> len(a)
10
``````

And with somewhat larger numbers:

``````> b = list(frange(0.0, 1000000.0, 0.1))
> b[-1]
999999.9
> len(b)
10000000
``````

The code is also available as a GitHub Gist.

## 一个更简单的无库版本

``````def frange(start=0, stop=1, jump=0.1):
nsteps = int((stop-start)/jump)
dy = stop-start
# f(i) goes from start to stop as i goes from 0 to nsteps
return [start + float(i)*dy/nsteps for i in range(nsteps)]``````

## 编辑

``````from fractions import Fraction

def rrange(start=0, stop=1, jump=0.1):
nsteps = int((stop-start)/jump)
return [Fraction(i, nsteps) for i in range(nsteps)]``````

[*]特别是`frange()`返回列表，而不是生成器。但这足以满足我的需求。

## A simpler library-less version

Aw, heck — I’ll toss in a simple library-less version. Feel free to improve on it[*]:

``````def frange(start=0, stop=1, jump=0.1):
nsteps = int((stop-start)/jump)
dy = stop-start
# f(i) goes from start to stop as i goes from 0 to nsteps
return [start + float(i)*dy/nsteps for i in range(nsteps)]
``````

The core idea is that `nsteps` is the number of steps to get you from start to stop and `range(nsteps)` always emits integers so there’s no loss of accuracy. The final step is to map [0..nsteps] linearly onto [start..stop].

## edit

If, like alancalvitti you’d like the series to have exact rational representation, you can always use Fractions:

``````from fractions import Fraction

def rrange(start=0, stop=1, jump=0.1):
nsteps = int((stop-start)/jump)
return [Fraction(i, nsteps) for i in range(nsteps)]
``````

[*] In particular, `frange()` returns a list, not a generator. But it sufficed for my needs.

## 回答 11

``````import numpy as np

np.arange(0.5,5,1.5)
>> [0.5, 2.0, 3.5, 5.0]

# OBS you will sometimes see stuff like this happening,
# so you need to decide whether that's not an issue for you, or how you are going to catch it.
>> [0.50000001, 2.0, 3.5, 5.0]``````

This can be done with numpy.arange(start, stop, stepsize)

``````import numpy as np

np.arange(0.5,5,1.5)
>> [0.5, 2.0, 3.5, 5.0]

# OBS you will sometimes see stuff like this happening,
# so you need to decide whether that's not an issue for you, or how you are going to catch it.
>> [0.50000001, 2.0, 3.5, 5.0]
``````

Note 1: From the discussion in the comment section here, “never use `numpy.arange()` (the numpy documentation itself recommends against it). Use numpy.linspace as recommended by wim, or one of the other suggestions in this answer”

Note 2: I have read the discussion in a few comments here, but after coming back to this question for the third time now, I feel this information should be placed in a more readable position.

## 回答 12

``````def frange(x, y, jump):
while x < y:
yield x
x += jump``````

``````>>>list(frange(0, 100, 0.1))[-1]
99.9999999999986``````

``````>>>list(frange(0, 100, 0.1))[-1]
99.9``````

## 解决方案1

``````from math import ceil

def frange2(start, stop, step):
n_items = int(ceil((stop - start) / step))
return (start + i*step for i in range(n_items))``````

## 解决方案2

``````def frange3(start, stop, step):
res, n = start, 1

while res < stop:
yield res
res = start + n * step
n += 1``````

``````>>>list(frange3(1, 0, -.1))
[]``````

``````from operator import gt, lt

def frange3(start, stop, step):
res, n = start, 0.
predicate = lt if start < stop else gt
while predicate(res, stop):
yield res
res = start + n * step
n += 1``````

``````>>>list(frange3(1, 0, -.1))
[1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3999999999999999, 0.29999999999999993, 0.19999999999999996, 0.09999999999999998]``````

## 解决方案3

``````from itertools import count
from itertools import takewhile

def any_range(start, stop, step):
start = type(start + step)(start)
return takewhile(lambda n: n < stop, count(start, step))``````

``````>>>list(any_range(Fraction(2, 1), Fraction(100, 1), Fraction(1, 3)))[-1]
299/3
>>>list(any_range(Decimal('2.'), Decimal('4.'), Decimal('.3')))
[Decimal('2'), Decimal('2.3'), Decimal('2.6'), Decimal('2.9'), Decimal('3.2'), Decimal('3.5'), Decimal('3.8')]``````

## 小心

``````no_proceed = (start < stop and step < 0) or (start > stop and step > 0)
if no_proceed: raise StopIteration``````

`StopIteration`如果要模拟`range`功能本身，最好的方法是raise 。

## 模拟范围

``````def any_range2(*args):
if len(args) == 1:
start, stop, step = 0, args[0], 1.
elif len(args) == 2:
start, stop, step = args[0], args[1], 1.
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('any_range2() requires 1-3 numeric arguments')

# here you can check for isinstance numbers.Real or use more specific ABC or whatever ...

start = type(start + step)(start)
return takewhile(lambda n: n < stop, count(start, step))``````

As kichik wrote, this shouldn’t be too complicated. However this code:

``````def frange(x, y, jump):
while x < y:
yield x
x += jump
``````

Is inappropriate because of the cumulative effect of errors when working with floats. That is why you receive something like:

``````>>>list(frange(0, 100, 0.1))[-1]
99.9999999999986
``````

While the expected behavior would be:

``````>>>list(frange(0, 100, 0.1))[-1]
99.9
``````

## Solution 1

The cumulative error can simply be reduced by using an index variable. Here’s the example:

``````from math import ceil

def frange2(start, stop, step):
n_items = int(ceil((stop - start) / step))
return (start + i*step for i in range(n_items))
``````

This example works as expected.

## Solution 2

No nested functions. Only a while and a counter variable:

``````def frange3(start, stop, step):
res, n = start, 1

while res < stop:
yield res
res = start + n * step
n += 1
``````

This function will work well too, except for the cases when you want the reversed range. E.g:

``````>>>list(frange3(1, 0, -.1))
[]
``````

Solution 1 in this case will work as expected. To make this function work in such situations, you must apply a hack, similar to the following:

``````from operator import gt, lt

def frange3(start, stop, step):
res, n = start, 0.
predicate = lt if start < stop else gt
while predicate(res, stop):
yield res
res = start + n * step
n += 1
``````

With this hack you can use these functions with negative steps:

``````>>>list(frange3(1, 0, -.1))
[1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3999999999999999, 0.29999999999999993, 0.19999999999999996, 0.09999999999999998]
``````

## Solution 3

You can go even further with plain standard library and compose a range function for the most of numeric types:

``````from itertools import count
from itertools import takewhile

def any_range(start, stop, step):
start = type(start + step)(start)
return takewhile(lambda n: n < stop, count(start, step))
``````

This generator is adapted from the Fluent Python book (Chapter 14. Iterables, Iterators and generators). It will not work with decreasing ranges. You must apply a hack, like in the previous solution.

You can use this generator as follows, for example:

``````>>>list(any_range(Fraction(2, 1), Fraction(100, 1), Fraction(1, 3)))[-1]
299/3
>>>list(any_range(Decimal('2.'), Decimal('4.'), Decimal('.3')))
[Decimal('2'), Decimal('2.3'), Decimal('2.6'), Decimal('2.9'), Decimal('3.2'), Decimal('3.5'), Decimal('3.8')]
``````

And of course you can use it with float and int as well.

## Be careful

If you want to use these functions with negative steps, you should add a check for the step sign, e.g.:

``````no_proceed = (start < stop and step < 0) or (start > stop and step > 0)
if no_proceed: raise StopIteration
``````

The best option here is to raise `StopIteration`, if you want to mimic the `range` function itself.

## Mimic range

If you would like to mimic the `range` function interface, you can provide some argument checks:

``````def any_range2(*args):
if len(args) == 1:
start, stop, step = 0, args[0], 1.
elif len(args) == 2:
start, stop, step = args[0], args[1], 1.
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('any_range2() requires 1-3 numeric arguments')

# here you can check for isinstance numbers.Real or use more specific ABC or whatever ...

start = type(start + step)(start)
return takewhile(lambda n: n < stop, count(start, step))
``````

I think, you’ve got the point. You can go with any of these functions (except the very first one) and all you need for them is python standard library.

## 回答 13

``````def drange(start,stop,step):
double_value_range = []
while start<stop:
a = str(start)
a.split('.')[1].split('0')[0]
start = float(str(a))
double_value_range.append(start)
start = start+step
double_value_range_tuple = tuple(double_value_range)
#print double_value_range_tuple
return double_value_range_tuple``````

i wrote a function that returns a tuple of a range of double precision floating point numbers without any decimal places beyond the hundredths. it was simply a matter of parsing the range values like strings and splitting off the excess. I use it for displaying ranges to select from within a UI. I hope someone else finds it useful.

``````def drange(start,stop,step):
double_value_range = []
while start<stop:
a = str(start)
a.split('.')[1].split('0')[0]
start = float(str(a))
double_value_range.append(start)
start = start+step
double_value_range_tuple = tuple(double_value_range)
#print double_value_range_tuple
return double_value_range_tuple
``````

## 用法

``````# Counting up
drange(0, 0.4, 0.1)
[0, 0.1, 0.2, 0.30000000000000004, 0.4]

# Counting down
drange(0, -0.4, -0.1)
[0, -0.1, -0.2, -0.30000000000000004, -0.4]``````

## 将每一步四舍五入到N个小数位

``````drange(0, 0.4, 0.1, round_decimal_places=4)
[0, 0.1, 0.2, 0.3, 0.4]

drange(0, -0.4, -0.1, round_decimal_places=4)
[0, -0.1, -0.2, -0.3, -0.4]``````

## 码

``````def drange(start, end, increment, round_decimal_places=None):
result = []
if start < end:
# Counting up, e.g. 0 to 0.4 in 0.1 increments.
if increment < 0:
raise Exception("Error: When counting up, increment must be positive.")
while start <= end:
result.append(start)
start += increment
if round_decimal_places is not None:
start = round(start, round_decimal_places)
else:
# Counting down, e.g. 0 to -0.4 in -0.1 increments.
if increment > 0:
raise Exception("Error: When counting down, increment must be negative.")
while start >= end:
result.append(start)
start += increment
if round_decimal_places is not None:
start = round(start, round_decimal_places)
return result``````

## 为什么选择这个答案？

• 当要求倒计时时，许多其他答案将挂起。
• 许多其他答案将给出错误的舍入结果。
• 其他答案`np.linspace`都是反复无常的，由于难以选择正确的除法数，它们可能会起作用，也可能不会起作用。`np.linspace`十进制增量为0.1确实很困难，公式中将增量转换为多个拆分的除法顺序可能会导致代码正确或损坏。
• `np.arange`不建议使用其他答案。

## Usage

``````# Counting up
drange(0, 0.4, 0.1)
[0, 0.1, 0.2, 0.30000000000000004, 0.4]

# Counting down
drange(0, -0.4, -0.1)
[0, -0.1, -0.2, -0.30000000000000004, -0.4]
``````

## To round each step to N decimal places

``````drange(0, 0.4, 0.1, round_decimal_places=4)
[0, 0.1, 0.2, 0.3, 0.4]

drange(0, -0.4, -0.1, round_decimal_places=4)
[0, -0.1, -0.2, -0.3, -0.4]
``````

## Code

``````def drange(start, end, increment, round_decimal_places=None):
result = []
if start < end:
# Counting up, e.g. 0 to 0.4 in 0.1 increments.
if increment < 0:
raise Exception("Error: When counting up, increment must be positive.")
while start <= end:
result.append(start)
start += increment
if round_decimal_places is not None:
start = round(start, round_decimal_places)
else:
# Counting down, e.g. 0 to -0.4 in -0.1 increments.
if increment > 0:
raise Exception("Error: When counting down, increment must be negative.")
while start >= end:
result.append(start)
start += increment
if round_decimal_places is not None:
start = round(start, round_decimal_places)
return result
``````

• Many other answers will give incorrectly rounded results.
• Other answers based on `np.linspace` are hit-and-miss, they may or may not work due to difficulty in choosing the correct number of divisions. `np.linspace` really struggles with decimal increments of 0.1, and the order of divisions in the formula to convert the increment into a number of splits can result in either correct or broken code.
• Other answers based on `np.arange` are deprecated.

If in doubt, try the four tests cases above.

## 回答 15

``````def Range(*argSequence):
if len(argSequence) == 3:
imin = argSequence[0]; imax = argSequence[1]; di = argSequence[2]
i = imin; iList = []
while i <= imax:
iList.append(i)
i += di
return iList
if len(argSequence) == 2:
return Range(argSequence[0], argSequence[1], 1)
if len(argSequence) == 1:
return Range(1, argSequence[0], 1)``````

``````def Range(*argSequence):
if len(argSequence) == 3:
imin = argSequence[0]; imax = argSequence[1]; di = argSequence[2]
i = imin; iList = []
while i <= imax:
iList.append(i)
i += di
return iList
if len(argSequence) == 2:
return Range(argSequence[0], argSequence[1], 1)
if len(argSequence) == 1:
return Range(1, argSequence[0], 1)
``````

Please note the first letter of Range is capital. This naming method is not encouraged for functions in Python. You can change Range to something like drange or frange if you want. The “Range” function behaves just as you want it to. You can check it’s manual here [ http://reference.wolfram.com/language/ref/Range.html ].

## 回答 16

``````def drange(start,stop=None,jump=1,approx=7): # Approx to 1e-7 by default
'''
This function is equivalent to range but for both float and integer
'''
if not stop: # If there is no y value: range(x)
stop= start
start= 0
valor= round(start,approx)
while valor < stop:
if valor==int(valor):
yield int(round(valor,approx))
else:
yield float(round(valor,approx))
valor += jump
for i in drange(12):
print(i)``````

I think that there is a very simple answer that really emulates all the features of range but for both float and integer. In this solution, you just suppose that your approximation by default is 1e-7 (or the one you choose) and you can change it when you call the function.

``````def drange(start,stop=None,jump=1,approx=7): # Approx to 1e-7 by default
'''
This function is equivalent to range but for both float and integer
'''
if not stop: # If there is no y value: range(x)
stop= start
start= 0
valor= round(start,approx)
while valor < stop:
if valor==int(valor):
yield int(round(valor,approx))
else:
yield float(round(valor,approx))
valor += jump
for i in drange(12):
print(i)
``````

## 回答 17

``arange = lambda start, stop, step: [i + step * i for i in range(int((stop - start) / step))]``

``arange(0, 1, 0.1)``

``[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]``

There will be of course some rounding errors, so this is not perfect, but this is what I use generally for applications, which don’t require high precision. If you wanted to make this more accurate, you could add an extra argument to specify how to handle rounding errors. Perhaps passing a rounding function might make this extensible and allow the programmer to specify how to handle rounding errors.

``````arange = lambda start, stop, step: [i + step * i for i in range(int((stop - start) / step))]
``````

If I write:

``````arange(0, 1, 0.1)
``````

It will output:

``````[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9]
``````

## 回答 18

``````def f_range(start, end, step):
a = range(int(start/0.01), int(end/0.01), int(step/0.01))
var = []
for item in a:
var.append(item*0.01)
return var``````

Is there a range() equivalent for floats in Python? NO Use this:

``````def f_range(start, end, step):
a = range(int(start/0.01), int(end/0.01), int(step/0.01))
var = []
for item in a:
var.append(item*0.01)
return var
``````

## 回答 19

``````def frange(start, stop=None, step=1):
if stop is None:
start, stop = 0, start
steps = int((stop-start)/step)
for i in range(steps):
yield start
start += step  ``````

There several answers here that don’t handle simple edge cases like negative step, wrong start, stop etc. Here’s the version that handles many of these cases correctly giving same behaviour as native `range()`:

``````def frange(start, stop=None, step=1):
if stop is None:
start, stop = 0, start
steps = int((stop-start)/step)
for i in range(steps):
yield start
start += step
``````

Note that this would error out step=0 just like native `range`. One difference is that native range returns object that is indexable and reversible while above doesn’t.

You can play with this code and test cases here.