How to use a decimal range() step value?

前端 未结 30 2122
醉话见心
醉话见心 2020-11-21 22:34

Is there a way to step between 0 and 1 by 0.1?

I thought I could do it like the following, but it failed:

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


        
相关标签:
30条回答
  • 2020-11-21 23:04
    [x * 0.1 for x in range(0, 10)] 
    

    in Python 2.7x gives you the result of:

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

    but if you use:

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

    gives you the desired:

    [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

    0 讨论(0)
  • 2020-11-21 23:04

    Suprised no-one has yet mentioned the recommended solution in the Python 3 docs:

    See also:

    • The linspace recipe shows how to implement a lazy version of range that suitable for floating point applications.

    Once defined, the recipe is easy to use and does not require numpy or any other external libraries, but functions like numpy.linspace(). Note that rather than a step argument, the third num argument specifies the number of desired values, for example:

    print(linspace(0, 10, 5))
    # linspace(0, 10, 5)
    print(list(linspace(0, 10, 5)))
    # [0.0, 2.5, 5.0, 7.5, 10]
    

    I quote a modified version of the full Python 3 recipe from Andrew Barnert below:

    import collections.abc
    import numbers
    
    class linspace(collections.abc.Sequence):
        """linspace(start, stop, num) -> linspace object
    
        Return a virtual sequence of num numbers from start to stop (inclusive).
    
        If you need a half-open range, use linspace(start, stop, num+1)[:-1].
        """
        def __init__(self, start, stop, num):
            if not isinstance(num, numbers.Integral) or num <= 1:
                raise ValueError('num must be an integer > 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))
    
    0 讨论(0)
  • 2020-11-21 23:04

    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
    
    0 讨论(0)
  • 2020-11-21 23:05

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

    0 讨论(0)
  • 2020-11-21 23:05

    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
    
    0 讨论(0)
  • 2020-11-21 23:06

    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 <type: Decimal>')
    
        # neglect bad cases
        if (start == stop) or (start > stop and step >= zero) or \
                              (start < stop and step <= zero):
            return []
    
        current = start
        while abs(current) < abs(stop):
            yield current
            current += step
    

    Sample outputs -

    from decimal import Decimal as D
    
    list(decimal_range(D('2')))
    # [Decimal('0'), Decimal('1')]
    list(decimal_range(D('2'), D('4.5')))
    # [Decimal('2'), Decimal('3'), Decimal('4')]
    list(decimal_range(D('2'), D('4.5'), D('0.5')))
    # [Decimal('2'), Decimal('2.5'), Decimal('3.0'), Decimal('3.5'), Decimal('4.0')]
    list(decimal_range(D('2'), D('4.5'), D('-0.5')))
    # []
    list(decimal_range(D('2'), D('-4.5'), D('-0.5')))
    # [Decimal('2'),
    #  Decimal('1.5'),
    #  Decimal('1.0'),
    #  Decimal('0.5'),
    #  Decimal('0.0'),
    #  Decimal('-0.5'),
    #  Decimal('-1.0'),
    #  Decimal('-1.5'),
    #  Decimal('-2.0'),
    #  Decimal('-2.5'),
    #  Decimal('-3.0'),
    #  Decimal('-3.5'),
    #  Decimal('-4.0')]
    
    0 讨论(0)
提交回复
热议问题