182

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
4

23 回答 23

140

您可以使用:

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

或使用 lambda / map:

map(lambda x: x/10.0, range(5, 50, 15))
于 2011-09-01T07:36:25.870 回答
107
我不知道内置函数,但是写一个像 [this](https://stackoverflow.com/a/477610/623735) 这样的函数应该不会太复杂。
def frange(x, y, jump):
  while x < y:
    yield x
    x += jump
---

正如评论所提到的,这可能会产生不可预测的结果,例如:

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

要获得预期的结果,您可以使用此问题中的其他答案之一,或者如@Tadhg 所述,您可以decimal.Decimal用作jump参数。确保使用字符串而不是浮点数对其进行初始化。

>>> 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

[编辑不是:如果您只使用正jump整数开始和停止 ( xand y) ,这可以正常工作。有关更通用的解决方案,请参见此处。]

于 2011-09-01T07:36:01.510 回答
95

numpy.arange由于浮点错误,我曾经使用过,但在控制它返回的元素数量时遇到了一些复杂问题。所以现在我使用linspace,例如:

>>> import numpy
>>> numpy.linspace(0, 10, num=4)
array([  0.        ,   3.33333333,   6.66666667,  10.        ])
于 2011-09-01T08:30:55.180 回答
41

Pylab 有frange(实际上是一个包装器 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. ])
于 2013-02-01T19:07:07.087 回答
14

热切评估(2.x range):

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

懒惰地评估(2.x xrange,3.x range):

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.
于 2011-09-01T07:40:16.300 回答
12

使用itertools:懒惰地评估浮点范围:

>>> 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]
于 2015-12-06T07:07:54.850 回答
7

我帮助将函数numeric_range添加到包more-itertools中。

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)
于 2017-04-22T18:28:04.223 回答
5

kichik 提供了一个没有 numpy 等依赖项的解决方案,但由于浮点运算,它经常表现出意外。正如blubberdiblub所指出的,其他元素很容易潜入结果中。例如naive_frange(0.0, 1.0, 0.1),将 yield0.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
        if x < y:
          yield x

因为乘法,舍入误差不会累积。使用epsilon处理乘法可能的舍入误差,即使问题当然可能在非常小的和非常大的两端出现。现在,正如预期的那样:

> 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

该代码也可作为GitHub Gist获得。

于 2016-03-18T18:19:43.013 回答
5

没有这样的内置函数,但您可以使用以下代码(Python 3 代码)来尽可能安全地完成 Python 允许的工作。

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 上提供的代码

于 2016-12-22T02:17:39.193 回答
4

为什么标准库中没有浮点范围实现?

正如此处所有帖子所表明的那样,没有浮点版本的range(). range()也就是说,如果我们考虑到该函数通常用作索引(当然,这意味着访问器)生成器,那么省略是有道理的。因此,当我们调用 时range(0,40),实际上是在说我们想要从 0 开始到 40 的 40 个值,但不包括 40 本身。

当我们认为索引生成与索引的数量和它们的值一样多时,range()在标准库中使用 float 实现就没有意义了。例如,如果我们调用函数frange(0, 10, 0.25),我们会期望 0 和 10 都包含在内,但这会产生一个有 41 个值的生成器,而不是 40 可能期望的值10/0.25

因此,根据其用途,frange()函数总是会表现出反直觉的行为;从索引的角度来看,它要么具有太多的值,要么不包括从数学角度合理地应该返回的数字。换句话说,很容易看出这样一个函数如何将两个非常不同的用例混为一谈——命名意味着索引用例;这种行为意味着一种数学行为。

数学用例

话虽如此,正如其他帖子中所讨论的,numpy.linspace()从数学角度很好地执行了生成:

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))

同样,我们也可以使用内置round函数并指定小数位数:

# 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

此外,当skip参数不能被值整除时stop,考虑到后一个问题,可能会出现巨大的差距:

# 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

有很多方法可以解决这个问题,但归根结底,最好的方法可能是只使用 Numpy。

于 2017-12-18T23:04:58.557 回答
4

这可以通过 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]

注1: 从这里评论部分的讨论中,“永远不要使用numpy.arange()(numpy文档本身建议反对它)。使用wim推荐的numpy.linspace,或此答案中的其他建议之一”

注2: 我已经阅读了这里的一些评论中的讨论,但是在第三次回到这个问题之后,我觉得这个信息应该放在一个更易读的位置。

于 2019-11-05T15:55:00.950 回答
3

更简单的无库版本

哦,见鬼——我会折腾一个简单的无库版本。随意改进它[*]:

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)]

核心思想是nsteps从开始到停止的步数,并且range(nsteps)始终发出整数,因此不会损失准确性。最后一步是将 [0..nsteps] 线性映射到 [start..stop] 上。

编辑

如果像alancalvitti一样,您希望该系列具有精确的理性表示,您可以随时使用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)]

[*] 特别是frange()返回一个列表,而不是一个生成器。但这足以满足我的需要。

于 2018-11-14T00:57:32.523 回答
3

正如kichik所写,这不应该太复杂。但是这段代码:

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))
[]

在这种情况下,解决方案 1 将按预期工作。要使此功能在这种情况下工作,您必须应用 hack,类似于以下内容:

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

有了这个 hack,你可以使用这些功能和消极的步骤:

>>>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))

这个生成器改编自 Fluent Python 书(第 14 章。可迭代对象、迭代器和生成器)。它不适用于递减范围。您必须应用 hack,就像在之前的解决方案中一样。

您可以按如下方式使用此生成器,例如:

>>>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')]

当然,您也可以将它与floatint一起使用。

当心

如果您想在负步骤中使用这些功能,您应该添加一个检查步骤符号,例如:

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

StopIteration如果你想模仿range函数本身,最好的选择是 raise 。

模仿范围

如果您想模仿range函数接口,可以提供一些参数检查:

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))

我想,你说得有道理。你可以使用这些函数中的任何一个(除了第一个函数),需要的只是 python 标准库。

于 2020-04-05T06:49:20.177 回答
2

用法

# 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已弃用。

如果有疑问,请尝试上面的四个测试用例。

于 2020-02-07T12:38:47.170 回答
1

我写了一个函数,它返回一个双精度浮点数范围的元组,没有任何超过百分之一的小数位。这只是解析范围值(如字符串)并分离多余部分的问题。我用它来显示范围以从 UI 中进行选择。我希望别人觉得它有用。

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
于 2015-11-19T04:53:40.433 回答
1

我不知道问题是否过时,但库中有一个arange函数NumPy,它可以作为一个范围工作。

np.arange(0,1,0.1)

#out: 

array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
于 2021-09-06T14:32:09.577 回答
0
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)

请注意 Range 的第一个字母是大写的。Python 中的函数不鼓励使用这种命名方法。如果需要,您可以将 Range 更改为 drange 或 frange 之类的内容。“范围”功能的行为就像您想要的那样。你可以在这里查看它的手册 [ http://reference.wolfram.com/language/ref/Range.html ]。

于 2017-01-12T09:17:20.973 回答
0

我认为有一个非常简单的答案可以真正模拟范围的所有功能,但适用于浮点数和整数。在此解决方案中,您只需假设默认情况下的近似值是 1e-7(或您选择的那个),您可以在调用函数时更改它。

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)
于 2017-10-14T12:51:15.213 回答
0

谈论用鼹鼠山造山。如果您放宽对range函数进行浮点模拟的要求,并且只需创建一个易于在for循环中使用的浮点列表,那么编码就简单而健壮。

def super_range(first_value, last_value, number_steps):
    if not isinstance(number_steps, int):
        raise TypeError("The value of 'number_steps' is not an integer.")
    if number_steps < 1:
        raise ValueError("Your 'number_steps' is less than 1.")

    step_size = (last_value-first_value)/(number_steps-1)

    output_list = []
    for i in range(number_steps):
        output_list.append(first_value + step_size*i)
    return output_list

first = 20.0
last = -50.0
steps = 5

print(super_range(first, last, steps))

输出将是

[20.0, 2.5, -15.0, -32.5, -50.0]

请注意,该功能super_range不限于浮点数。它可以处理定义了运算符+-*/的任何数据类型,例如complexDecimalnumpy.array

import cmath
first = complex(1,2)
last = complex(5,6)
steps = 5

print(super_range(first, last, steps))

from decimal import *
first = Decimal(20)
last = Decimal(-50)
steps = 5

print(super_range(first, last, steps))

import numpy as np
first = np.array([[1, 2],[3, 4]])
last = np.array([[5, 6],[7, 8]])
steps = 5

print(super_range(first, last, steps))

输出将是:

[(1+2j), (2+3j), (3+4j), (4+5j), (5+6j)]
[Decimal('20.0'), Decimal('2.5'), Decimal('-15.0'), Decimal('-32.5'), Decimal('-50.0')]
[array([[1., 2.],[3., 4.]]),
 array([[2., 3.],[4., 5.]]),
 array([[3., 4.],[5., 6.]]),
 array([[4., 5.],[6., 7.]]),
 array([[5., 6.],[7., 8.]])]
于 2020-09-01T22:21:08.633 回答
0

虽然基于整数的范围在“所见即所得”中得到了很好的定义,但有些东西在浮点数中不容易看到,这会导致在所需范围内获得看似明确定义的行为时遇到麻烦。

有两种方法可以采取:

  1. 将给定范围分成一定数量的段:linspace 方法,当您选择不能很好地划分跨度的点数时,您接受大量十进制数字(例如,7 步中的 0 到 1 将给出第一个步长值为 0.14285714285714285)

  2. 给出你已经知道应该工作并希望它工作的所需 WYSIWIG 步长。您的希望通常会因获得的值错过您想要达到的终点而破灭。

倍数可能高于或低于您的预期:

>>> 3*.1 > .3  # 0.30000000000000004
True

>>> 3*.3 < 0.9  # 0.8999999999999999
True

您将尝试通过添加步数的倍数而不是递增来避免累积错误,但问题总是会出现,如果您在纸上手工完成,您将无法得到您期望的结果 - 使用精确的小数。但是您知道这应该是可能的,因为 Python 向您显示0.1了接近 0.1 的基础整数比率,而不是:

>>> (3*.1).as_integer_ratio()
(1351079888211149, 4503599627370496)

在作为答案提供的方法中,最好在此处使用 Fraction 以及将输入作为字符串处理的选项。我有一些建议可以让它变得更好:

  1. 让它处理类似范围的默认值,这样你就可以自动从 0 开始
  2. 使其处理递减范围
  3. 如果您使用精确算术,使输出看起来像您期望的那样

我提供了一个做这些相同事情但不使用 Fraction 对象的例程。相反,它用于round创建具有与使用 python 打印的数字相同的明显数字的数字,例如 0.1 之类的 1 个小数和 0.004 之类的 3 个小数:

def frange(start, stop, step, n=None):
    """return a WYSIWYG series of float values that mimic range behavior
    by excluding the end point and not printing extraneous digits beyond
    the precision of the input numbers (controlled by n and automatically
    detected based on the string representation of the numbers passed).

    EXAMPLES
    ========

    non-WYSIWYS simple list-comprehension

    >>> [.11 + i*.1 for i in range(3)]
    [0.11, 0.21000000000000002, 0.31]

    WYSIWYG result for increasing sequence

    >>> list(frange(0.11, .33, .1))
    [0.11, 0.21, 0.31]

    and decreasing sequences

    >>> list(frange(.345, .1, -.1))
    [0.345, 0.245, 0.145]

    To hit the end point for a sequence that is divisibe by
    the step size, make the end point a little bigger by
    adding half the step size:

    >>> dx = .2
    >>> list(frange(0, 1 + dx/2, dx))
    [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

    """
    if step == 0:
        raise ValueError('step must not be 0')
    # how many decimal places are showing?
    if n is None:
        n = max([0 if '.' not in str(i) else len(str(i).split('.')[1])
                for i in (start, stop, step)])
    if step*(stop - start) > 0:  # a non-null incr/decr range
        if step < 0:
            for i in frange(-start, -stop, -step, n):
                yield -i
        else:
            steps = round((stop - start)/step)
            while round(step*steps + start, n) < stop:
                steps += 1
            for i in range(steps):
                yield round(start + i*step, n)
于 2021-04-12T06:43:41.693 回答
-1

Python 中的浮点数是否有 range() 等效项?否 使用这个:

def f_range(start, end, step, coef=0.01):
    a = range(int(start/coef), int(end/coef), int(step/coef))
    var = []
    for item in a:
        var.append(item*coef)
    return var
于 2017-01-12T10:00:28.200 回答
-1

当然会有一些舍入误差,所以这并不完美,但这是我通常用于不需要高精度的应用程序的方法。如果你想让这个更准确,你可以添加一个额外的参数来指定如何处理舍入错误。也许传递一个舍入函数可能会使它具有可扩展性,并允许程序员指定如何处理舍入错误。

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]
于 2020-03-30T02:37:26.423 回答
-2

这里有几个答案不能处理简单的边缘情况,如负步、错误开始、停止等。这是正确处理许多这些情况的版本,给出与 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  

请注意,这会像 native 一样出错 step=0 range。一个区别是本机范围返回的对象是可索引和可​​逆的,而上面没有。

您可以在此处使用此代码和测试用例。

于 2019-03-02T00:04:52.783 回答