2010-11-15 4 views
38

The documentation в основном говорит, что range должны вести себя именно так, как эту реализацию (для положительного step):диапазона с шагом типа поплавка

def range(start, stop, step): 
    x = start 
    while True: 
    if x >= stop: return 
    yield x 
    x += step 

Он также говорит, что его аргументы должны быть целыми числами. Почему это? Разве это определение не является абсолютно корректным, если шаг является поплавком?

В моем случае, я особенно. нуждающаяся в функции range, которая принимает тип float как свой аргумент step. Есть ли в Python или мне нужно реализовать свои собственные?


Более конкретно: Как бы перевести этот код C непосредственно на Python хорошим способом (т.е. не просто делать это через while -loop вручную):

for(float x = 0; x < 10; x += 0.5f) { /* ... */ } 
+0

В этом цикле вы не можете использовать слова 'return' и' yield', используйте 'break'. –

+4

@ Тим: Конечно, я могу. – Albert

+0

Похоже, вы можете! Я не думал, что это возможно .. Я уверен, что ошибки были подняты, когда я попробовал что-то подобное. –

ответ

44

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

EDIT: Документы предпочитают numpy.linspace. Thanks @Droogans for noticeing =)

+3

В первом абзаце пункта: При использовании нецелого шага, такого как 0,1, результаты часто не будут согласованы. Для этих случаев лучше использовать ['linspace'] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html?highlight=linspace#numpy.linspace). – Droogans

+2

Обратите внимание, что 'linspace' имеет другой интерфейс. –

+0

Или даже без numpy.arange. Я выполнил функцию xfrange без проблем с поплавковой точностью. Проверьте это;) http://stackoverflow.com/questions/477486/python-decimal-range-step-value/20549652#20549652 –

-1

Вероятно потому, что вы можете» t имеют часть итерации. Кроме того, floats являются неточными.

+0

Поплавки очень точные. То, что они не подходят, представляет собой определенные значения, которые имеют конечное представление в базе 10. Например, 0,1 в базе 10 станет 0x1.999999999999ap-4 (с использованием точного шестнадцатеричного представления с плавающей точкой) в вашей памяти, который не является точно 0,1 в десятичной , С этим аргументом вы также можете сказать, что десятичные числа являются неточными, потому что есть определенные значения, что у них нет конечного представления для ... – josch

4

Когда вы добавляете числа с плавающей точкой вместе, часто возникает небольшая ошибка. Будет ли range(0.0, 2.2, 1.1) возвращаться [0.0, 1.1] или [0.0, 1.1, 2.199999999]? Нельзя быть уверенным без тщательного анализа.

Код, который вы опубликовали, является ОК, если вам это действительно нужно. Просто знайте о возможных недостатках.

+1

Да, есть способ быть уверенным. Размер шага 1.1 равен 0x1.199999999999ap + 0, если он представлен как двойной номер с плавающей запятой в вашей памяти. Код, который был опубликован OP, не является идеальным решением, поскольку повторное добавление неточных значений позволяет скопировать ошибку. Лучше всего отслеживать число циклов во время итерации и умножать размер шага на число циклов, как уже показали другие ответы. – josch

+0

@josch Я имел в виду в общем случае, а не эти цифры конкретно. –

+0

Тогда наши определения «определенные» отличаются. Даже с числами с плавающей запятой компьютер не даст вам «неопределенный» или «случайный» результат. Результат всегда будет детерминированным. Независимо от ввода, учитывая любые конечные числа с плавающей точкой, вы всегда сможете с уверенностью сказать, каков их результат после добавления X времени с заданной точностью. – josch

30

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

range(0, 0.4, 0.1) 

можно было бы ожидать выход

[0, 0.1, 0.2, 0.3] 

но вы на самом деле получить что-то вроде

[0, 0.1, 0.2000000001, 0.3000000001] 

из-за округления вопросов. А поскольку диапазон часто используется для генерации индексов какого-то типа, это целые числа.

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

def xfrange(start, stop, step): 
    i = 0 
    while start + i * step < stop: 
     yield start + i * step 
     i += 1 
+0

Да, конечно (естественно, что он ведет себя так). Это всегда происходит с поплавками. Поэтому, конечно, было бы вполне законным, что если 'range' будет поддерживать float, он будет вести себя именно так.Поэтому я действительно не понимаю, почему это не должно. – Albert

+0

Да, ты прав. Лучшее, что я могу придумать, - «это неточно». И «возможно, это необходимо для некоторых общих деталей реализации?» – Zarkonnen

+1

@Albert: обычно не так много думают, почему такие решения были сделаны; они просто были. В этом случае ограниченные варианты использования будут перегружены потенциалом для отвратительных ошибок imho. – katrielalex

6

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

Что вы действительно хотите, это арифметическая прогрессия; следующий код будет работать довольно успешно для int, float и complex ... и строк и списков ...

def arithmetic_progression(start, step, length): 
    for i in xrange(length): 
     yield start + i * step 

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

>>> 10000 * 0.0001, sum(0.0001 for i in xrange(10000)) 
(1.0, 0.9999999999999062) 
>>> 10000 * (1/3.), sum(1/3. for i in xrange(10000)) 
(3333.333333333333, 3333.3333333337314) 

Исправление: вот competetive running-total gadget:

def kahan_range(start, stop, step): 
    assert step > 0.0 
    total = start 
    compo = 0.0 
    while total < stop: 
     yield total 
     y = step - compo 
     temp = total + y 
     compo = (temp - total) - y 
     total = temp 

>>> list(kahan_range(0, 1, 0.0001))[-1] 
0.9999 
>>> list(kahan_range(0, 3333.3334, 1/3.))[-1] 
3333.333333333333 
>>> 
+0

Это замечательный ответ. Почему он не получил больше авансов, чем ответы, которые накапливают неточность или нуждаются в дополнительных библиотеках? – josch

12

Для того, чтобы иметь возможность использовать десятичных чисел в выражении диапазона прохладный путь сделать это состоит в следующем: [х * 0,1 для х в диапазоне (0, 10)]

+2

Если ваш «диапазон» имеет много номеров, а ваш «шаг» 0,1 меньше, скажем .00000001, то это не сработает, и скрипт будет просто зависать на большинстве компьютеров. Нам нужно что-то, что имитирует то, что xrange делает. нам нужен итерируемый/генератор, а не список comprehension.fxrange диапазон выше работает лучше в этом случае. – ekta

+1

Очень простое решение для понимания списка, спасибо! – avtomaton

+1

@ekta Используйте '()' вместо '[]', чтобы превратить это в генератор. –

3

Вот особый случай, который может быть достаточно хорошим:

[ (1.0/divStep)*x for x in range(start*divStep, stop*divStep)] 

В вашем случае это будет:

#for(float x = 0; x < 10; x += 0.5f) { /* ... */ } ==> 
start = 0 
stop = 10 
divstep = 1/.5 = 2 #This needs to be int, thats why I said 'special case' 

и так:

>>> [ .5*x for x in range(0*2, 10*2)] 
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5] 
1

Это то, что я хотел бы использовать:

numbers = [float(x)/10 for x in range(10)] 

, а не:

numbers = [x*0.1 for x in range(10)] 
that would return : 
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9] 

надеюсь, что он LPS.

+0

Это не дает ответа на вопрос. Чтобы критиковать или запросить разъяснения у автора, оставьте комментарий ниже своего сообщения - вы всегда можете прокомментировать свои собственные сообщения, и как только у вас будет достаточно [репутации] (http://stackoverflow.com/help/whats-reputation), вы будете быть в состоянии [прокомментировать любое сообщение] (http://stackoverflow.com/help/privileges/comment). –

+0

@RaisAlam Обратите внимание, что если бы он использовал 0,5 вместо 0,1, неожиданное поведение не появлялось. –

Смежные вопросы