Как использовать десятичное значение шага ()?


744

Есть ли способ перейти между 0 и 1 на 0,1?

Я думал, что смогу сделать это следующим образом, но это не удалось:

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

Вместо этого он говорит, что аргумент шага не может быть нулем, чего я не ожидал.


17
int (0.1) == 0, поэтому шаг фактически равен нулю. Это может быть неожиданным, но это ноль. Вы можете повторить свой вопрос, чтобы отразить тот факт, что вы этого не ожидали. Сказать «это не так» неверно и вводит в заблуждение.
S.Lott

3
Кстати, короткий однострочник можно свернуть, используя itertools.takewhileи itertools.count. Это не лучше, чем drangeс точки зрения производительности.
Кос

1
Стыдно, что диапазон python не позволяет этого, учитывая, как легко реализовать генератор, который делает это даже без накопления ошибок округления. Черт, даже seqинструмент в GNU coreutils позволяет обходиться seq 0 0.1 1без ошибок округления!
2015 года

3
@josch: seqиспользует C long doubleтип внутренне, и это с учетом ошибок округления. Например, на моей машине, seq 0 0.1 1дает 1как последний вывод (как и ожидалось), но seq 1 0.1 2дает 1.9как последний вывод (а не ожидаемый 2).
Марк Дикинсон

Для удобства предложение @ Kos может быть реализовано как itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))или itertools.islice(itertools.count(0,0.1), 10)(после того, как вы это сделали import itertools), хотя я не проверял, что является более эффективным
Anonymous

Ответы:


906

Вместо того, чтобы использовать десятичный шаг напрямую, гораздо безопаснее выразить это в виде количества точек, которые вы хотите получить. В противном случае ошибка округления с плавающей точкой может привести к неверному результату.

Вы можете использовать функцию linspace из библиотеки NumPy (которая не является частью стандартной библиотеки, но ее относительно легко получить). linspaceвозвращает количество точек для возврата, а также позволяет указать, следует ли включать правильную конечную точку:

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

Если вы действительно хотите использовать значение шага с плавающей точкой, вы можете, с помощью 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])

Ошибка округления чисел с плавающей точкой будет вызывать проблемы, хотя. Вот простой случай, когда ошибка округления приводит arangeк созданию массива длины 4, когда он должен производить только 3 числа:

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

51
numpy - это такой вездесущий компонент python, что я считаю этот ответ самым «питоническим» из всех.
авиаудар

21
@AndreTerra Проблема в том, что @ numpy @ является сторонним пакетом и добавляет много накладных расходов с точки зрения управления зависимостями, хранения (для самого пакета) и т. Д. В зависимости от того, что делает разработчик, использование может оказаться невозможным Это.
rbaleksandar

Извините, но я не понял ошибку округления с плавающей запятой в последней части с тех пор np.linspace(1.,1.3,4)и np.arange(1.,1.3,0.1)
выдаю

4
@deadcode Причина в том, что np.arange определен для создания диапазона [start,stop)(т.е. исключая stop), поэтому не стоит ожидать, что 1.3 будет включено в список. Посмотрите этот вопрос, почему он все еще включен и что с этим делать.
Денис

5
Количество использованного пакета, возможно, не является показателем того, является ли он «Pythonic».
Алекс Холл

213

Range () Python может делать только целые числа, а не с плавающей точкой. В вашем конкретном случае вы можете использовать понимание списка вместо:

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

(Замените вызов range этим выражением.)

В более общем случае вы можете написать пользовательскую функцию или генератор.


36
Более того, вы можете просто использовать генератор понимания, если вы работаете с Python 2.4+. (x * 0.1 for x in range(0, 10)),
JAB

42
Еще лучше поставить x/10вместо x * 0.1: D Ничего особенного на самом деле, но некоторые цифры там будут более точными, например, для 3*0.1вас 0.30000000000000004, в то время как для 3/10 вы получаете 0.3:)
Bloke

4
3/10 дает мне 0, а не 0,3. 3 / 10,0 дает 0,29999999999999999. Python 2.6.

19
@LarsWirzenius: в Python 2.2+ from __future__ import division; 3/10возвращает 0.3. Это поведение по умолчанию в Python 3.x.
Бенджамин Ходжсон

2
Функция round также может быть использована lst = [round (x * 0.10,2) для x в диапазоне (0,10)]
MARK

148

Основываясь на 'xrange ([start], stop [, step])' , вы можете определить генератор, который принимает и производит любой тип, который вы выберете (придерживайтесь типов, поддерживающих +и <):

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

45
Это имеет проблемы округления. Пожалуйста, посмотрите здесь: code.activestate.com/recipes/66472
Кристиан Оудард,

Я бы немного расширил его для другого направления с (в то время как r> stop) и соответствующим шагом r - = для указания противоположного направления.
user318904

1
Я сделал функцию xfrange без проблем точности с плавающей точкой, упомянутых выше. Проверьте это;) stackoverflow.com/questions/477486/…
Карлос Вега

1
Вы накапливаете ошибки округления. Пожалуйста, используйте это вместо: `i = 0; r = начать, а r <остановить: i + = 1; r = start + i * step; yield r`
Cees Timmerman

1
Это из pythoncentral.io/pythons-range-function-explained (и других источников документации Python)
Apostolos

31

Увеличьте величину iцикла, а затем уменьшите ее, когда вам это нужно.

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

РЕДАКТИРОВАТЬ: я честно не могу вспомнить, почему я думал, что это будет работать синтаксически

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

Это должно иметь желаемый результат.


Я думаю, вы обнаружите, что range () работает с целыми числами, и в этом случае это будет единственное решение, использующее ту же функцию по крайней мере.
Мэтью Шарли

1
@cmsjr creative: D Небольшая вещь: разделите на 100.0, чтобы Python не усек результат, если вы используете Python 2.x. Я думаю, что в 3.0 это будет работать так, как вы это написали.
Дана

2
for i * 100 in range(0, 100, 10): SyntaxError: невозможно назначить оператору
Энн ван Россум

25

scipyимеет встроенную функцию, arangeкоторая обобщает range()конструктор Python для удовлетворения ваших требований обработки с плавающей точкой.

from scipy import arange


8
На самом деле это то же самое, что arangeвы можете найти в numpy: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangeвернется True.
iFreilicht

from nump import arange as range
строптивая

24

Думаю, NumPy немного излишне.

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

Вообще говоря, чтобы сделать шаг за 1/xвверх , чтобы yвы могли бы сделать

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при тестировании меньше шума при округлении).


18

Подобно функции R seq , эта функция возвращает последовательность в любом порядке, учитывая правильное значение шага. Последнее значение равно значению остановки.

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

Результаты

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]


2
Это отличный ответ для тех, кто хочет получить его, не слишком углубляясь в python.
Чани

1
Это было почти то, что я искал - обратите внимание, что seq(0.5, 3.0)возвращается [0.5, 1.5, 2.5, 3.5]. Чтобы избежать последних записей быть вне диапазона, замените n = int(round(...с n = int(floor(...с линией from math import floorв верхней части (выше def seq(...).
FriendFX

2
@FriendFX Не делай этого! Если floorиспользуется, seq(0.2, 0.9, 0.1)не достигнет правильной конечной точки и вернется[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin

@ user502144: Хороший улов, спасибо. Я предполагаю, что должен согласиться на одно из более сложных решений, чтобы оно было общим.
FriendFX

14

Боюсь, встроенная функция range () возвращает последовательность целочисленных значений, поэтому вы не можете использовать ее для выполнения десятичного шага.

Я бы сказал, просто используйте цикл while:

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

Если вам интересно, Python конвертирует ваш 0.1 в 0, поэтому он говорит вам, что аргумент не может быть нулевым.


2
Не делай этого! Добавление .110 раз - это не то же самое, что добавление 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
кошка,

12

Вот решение с использованием itertools :

import itertools

def seq(start, end, step):
    if step == 0:
        raise ValueError("step must not be 0")
    sample_count = int(abs(end - start) / step)
    return itertools.islice(itertools.count(start, step), sample_count)

Пример использования:

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

Ради полноты вы должны рассчитать абсолютное значение для переменной sample_count, чтобы ваша функция также работала для отрицательного пуска (т.
Е.

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

в Python 2.7x дает вам результат:

[0,0, 0,1, 0,2, 0,30000000000000004, 0,4, 0,5, 0,6000000000000001, 0,7000000000000001, 0,8, 0,9]

но если вы используете:

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

дает вам желаемое:

[0,0, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7, 0,8, 0,9]



5

И если вы делаете это часто, вы можете сохранить сгенерированный список r

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

5

more_itertoolsсторонняя библиотека, которая реализует numeric_rangeинструмент:

import more_itertools as mit


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

Вывод

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Этот инструмент также работает для Decimalи Fraction.


4

Мои версии используют оригинальную функцию диапазона для создания мультипликативных индексов для сдвига. Это позволяет использовать тот же синтаксис для исходной функции диапазона. Я сделал две версии, одну с использованием float, а другую с использованием Decimal, потому что я обнаружил, что в некоторых случаях я хотел избежать дрейфа округления, представленного арифметикой с плавающей запятой.

Это согласуется с пустыми результатами набора, как в range / xrange.

Передача только одного числового значения в любую функцию вернет стандартный выходной диапазон к целочисленному предельному значению входного параметра (поэтому, если вы зададите 5.5, он вернет диапазон (6).)

Изменить: код ниже теперь доступен в виде пакета на Pypi: Franges

## frange.py
from math import ceil
# find best range function available to version (2.7.x / 3.x.x)
try:
    _xrange = xrange
except NameError:
    _xrange = range

def frange(start, stop = None, step = 1):
    """frange generates a set of floating point values over the 
    range [start, stop) with step size step

    frange([start,] stop [, step ])"""

    if stop is None:
        for x in _xrange(int(ceil(start))):
            yield x
    else:
        # create a generator expression for the index values
        indices = (i for i in _xrange(0, int((stop-start)/step)))  
        # yield results
        for i in indices:
            yield start + step*i

## drange.py
import decimal
from math import ceil
# find best range function available to version (2.7.x / 3.x.x)
try:
    _xrange = xrange
except NameError:
    _xrange = range

def drange(start, stop = None, step = 1, precision = None):
    """drange generates a set of Decimal values over the
    range [start, stop) with step size step

    drange([start,] stop, [step [,precision]])"""

    if stop is None:
        for x in _xrange(int(ceil(start))):
            yield x
    else:
        # find precision
        if precision is not None:
            decimal.getcontext().prec = precision
        # convert values to decimals
        start = decimal.Decimal(start)
        stop = decimal.Decimal(stop)
        step = decimal.Decimal(step)
        # create a generator expression for the index values
        indices = (
            i for i in _xrange(
                0, 
                ((stop-start)/step).to_integral_value()
            )
        )  
        # yield results
        for i in indices:
            yield float(start + step*i)

## testranges.py
import frange
import drange
list(frange.frange(0, 2, 0.5)) # [0.0, 0.5, 1.0, 1.5]
list(drange.drange(0, 2, 0.5, precision = 6)) # [0.0, 0.5, 1.0, 1.5]
list(frange.frange(3)) # [0, 1, 2]
list(frange.frange(3.5)) # [0, 1, 2, 3]
list(frange.frange(0,10, -1)) # []

Как может frangeработать, если остановка есть None? Эта часть кода даже не учитывает размер шага.
2016 года

1
@josch rangeимеет две подписи:, range(stop)которая предполагает значение по умолчанию start=0, step=1, и range(start, stop, step), где не делается никаких предположений. frangeотражает это. При использовании range(stop)подписи оба frangeи drangeначинаются с 0, и увеличиваются на 1, поэтому их поведение идентично обычному range(stop)поведению со стопом, округленным до ближайшего целого числа.
Nisan.H

4

Это мое решение, чтобы получить диапазоны с плавающими шагами.
При использовании этой функции нет необходимости ни импортировать numpy, ни устанавливать его.
Я уверен, что это можно улучшить и оптимизировать. Не стесняйтесь делать это и размещать здесь.

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)

Выход:

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

1
Произошла ошибка с пропуском последнего значения, если оно находится в пределах 1 шага от значения остановки. то есть xfrange (1,10,2) делает только 1,3,5,7, пропуская 9
Лоб

Для справки и других читателей, пожалуйста, сравните эту реализацию с этим stackoverflow.com/a/477610/54964 . Это, кажется, не имеет больших проблем с плавающей точкой.
Лео Леопольд Герц 준영

@carlosvega Можете ли вы подтвердить, почему Лобе получает свой результат?
Лео Леопольд Герц 준영

3

Для полноты бутика, функциональное решение:

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)

2

Вы можете использовать эту функцию:

def frange(start,end,step):
    return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))

Кажется, не работает правильно, напримерlist(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Шаи Коулман

2

Хитрость , чтобы избежать округления проблемы заключается в использовании отдельного номера для перемещения по диапазону, который начинается и половина шаг впереди старта .

# floating point range
def frange(a, b, stp=1.0):
  i = a+stp/2.0
  while i<b:
    yield a
    a += stp
    i += stp

В качестве альтернативы numpy.arangeможно использовать.


2

Это можно сделать с помощью библиотеки Numpy. Функция arange () позволяет выполнять шаги в float. Но он возвращает пустой массив, который может быть преобразован в список с помощью tolist () для нашего удобства.

for i in np.arange(0, 1, 0.1).tolist():
   print i

2

Мой ответ похож на другие, использующие map (), без необходимости использования NumPy и без использования лямбды (хотя вы могли бы). Чтобы получить список значений с плавающей точкой от 0,0 до t_max с шагом dt:

def xdt(n):
    return dt*float(n)
tlist  = map(xdt, range(int(t_max/dt)+1))

2

Удивленный, никто еще не упомянул рекомендуемое решение в документации по Python 3 :

Смотрите также:

  • В рецепте LINSPACE показывает , как реализовать ленивый вариант диапазона , который подходит для плавающих приложений точки.

После определения, рецепт прост в использовании и не требует numpyкаких-либо других внешних библиотек, но функционирует как numpy.linspace(). Обратите внимание, что вместо stepаргумента третий numаргумент задает количество желаемых значений, например:

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

Я цитирую модифицированную версию полного рецепта Python 3 от Эндрю Барнерта ниже:

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

2

Чтобы противостоять проблемам точности поплавка, вы можете использовать Decimalмодуль .

Это требует дополнительных усилий для преобразования Decimalиз intили floatво время написания кода, но вместо этого вы можете передать strи изменить функцию, если такое удобство действительно необходимо.

from decimal import Decimal
from decimal import Decimal as D


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

Пример выходов -

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

2
С аналогичными Decimalвходами, np.arangeработает так же:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj

2
Да, спасибо. Хотя для этого нужна внешняя (тупая) библиотека.
shad0w_wa1k3r

1

Добавьте автокоррекцию для возможности неверного знака на шаге:

def frange(start,step,stop):
    step *= 2*((stop>start)^(step<0))-1
    return [start+i*step for i in range(int((stop-start)/step))]

1

Мое решение:

def seq(start, stop, step=1, digit=0):
    x = float(start)
    v = []
    while x <= stop:
        v.append(round(x,digit))
        x += step
    return v

1

Лучшее решение: нет ошибки округления
_________________________________________________________________________________

>>> step = .1
>>> N = 10     # number of data points
>>> [ x / pow(step, -1) for x in range(0, N + 1) ]

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

_________________________________________________________________________________

Или для заданного диапазона вместо заданных точек данных (например, непрерывная функция) используйте:

>>> step = .1
>>> rnge = 1     # NOTE range = 1, i.e. span of data points
>>> N = int(rnge / step
>>> [ x / pow(step,-1) for x in range(0, N + 1) ]

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

Для реализации функции: заменить x / pow(step, -1)на f( x / pow(step, -1) )и определить f.
Например:

>>> import math
>>> def f(x):
        return math.sin(x)

>>> step = .1
>>> rnge = 1     # NOTE range = 1, i.e. span of data points
>>> N = int(rnge / step)
>>> [ f( x / pow(step,-1) ) for x in range(0, N + 1) ]

[0.0, 0.09983341664682815, 0.19866933079506122, 0.29552020666133955, 0.3894183423086505, 
 0.479425538604203, 0.5646424733950354, 0.644217687237691, 0.7173560908995228,
 0.7833269096274834, 0.8414709848078965]

1

запуск и остановка включаются, а не один или другой (обычно остановка исключается) и без импорта и с использованием генераторов

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, 8 июля 2017, 04:57:36) [MSC v.1900 64 бит (AMD64)]


1

Я знаю, что опаздываю на вечеринку здесь, но вот тривиальное генераторное решение, которое работает в 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

тогда вы можете назвать его так же, как оригинал range()... обработки ошибок нет, но дайте мне знать, если есть ошибка, которая может быть разумно обнаружена, и я обновлю. или вы можете обновить его. это StackOverflow.


0

Вот мое решение, которое отлично работает с float_range (-1, 0, 0.01) и работает без ошибок представления с плавающей запятой. Это не очень быстро, но отлично работает:

from decimal import Decimal

def get_multiplier(_from, _to, step):
    digits = []
    for number in [_from, _to, step]:
        pre = Decimal(str(number)) % 1
        digit = len(str(pre)) - 2
        digits.append(digit)
    max_digits = max(digits)
    return float(10 ** (max_digits))


def float_range(_from, _to, step, include=False):
    """Generates a range list of floating point values over the Range [start, stop]
       with step size step
       include=True - allows to include right value to if possible
       !! Works fine with floating point representation !!
    """
    mult = get_multiplier(_from, _to, step)
    # print mult
    int_from = int(round(_from * mult))
    int_to = int(round(_to * mult))
    int_step = int(round(step * mult))
    # print int_from,int_to,int_step
    if include:
        result = range(int_from, int_to + int_step, int_step)
        result = [r for r in result if r <= int_to]
    else:
        result = range(int_from, int_to, int_step)
    # print result
    float_result = [r / mult for r in result]
    return float_result


print float_range(-1, 0, 0.01,include=False)

assert float_range(1.01, 2.06, 5.05 % 1, True) ==\
[1.01, 1.06, 1.11, 1.16, 1.21, 1.26, 1.31, 1.36, 1.41, 1.46, 1.51, 1.56, 1.61, 1.66, 1.71, 1.76, 1.81, 1.86, 1.91, 1.96, 2.01, 2.06]

assert float_range(1.01, 2.06, 5.05 % 1, False)==\
[1.01, 1.06, 1.11, 1.16, 1.21, 1.26, 1.31, 1.36, 1.41, 1.46, 1.51, 1.56, 1.61, 1.66, 1.71, 1.76, 1.81, 1.86, 1.91, 1.96, 2.01]

0

Я только новичок, но у меня была такая же проблема при моделировании некоторых вычислений. Вот как я пытался решить эту проблему, которая, кажется, работает с десятичными шагами.

Я также довольно ленив, и поэтому мне было трудно написать свою собственную функцию диапазона.

В основном то , что я сделал это изменило мое xrange(0.0, 1.0, 0.01)к xrange(0, 100, 1)и используется деление на 100.0внутри цикла. Меня также беспокоило, будут ли ошибки округления. Поэтому я решил проверить, есть ли такие. Теперь я слышал, что если, например, 0.01из расчета не совсем поплавок, 0.01сравнивающий их должен вернуть False (если я ошибаюсь, пожалуйста, дайте мне знать).

Поэтому я решил проверить, будет ли мое решение работать для моего диапазона, выполнив короткий тест:

for d100 in xrange(0, 100, 1):
    d = d100 / 100.0
    fl = float("0.00"[:4 - len(str(d100))] + str(d100))
    print d, "=", fl , d == fl

И это напечатало True для каждого.

Теперь, если я все понял неправильно, пожалуйста, дайте мне знать.


0

Этот лайнер не будет загромождать ваш код. Знак параметра шага важен.

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)]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.