Як використовувати значення крокового значення десяткового діапазону ()?


742

Чи є спосіб перейти між 0 і 1 на 0,1?

Я думав, що можу це зробити так, але це не вдалося:

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

Натомість, це говорить, що аргумент кроку не може бути нульовим, чого я не очікував.


17
int (0,1) == 0, тому крок насправді дорівнює нулю. Це може бути несподівано, але це нуль. Ви можете повторно поставити своє запитання, щоб відобразити той факт, що ви цього не очікували. Говорити "це не так" - помилково і вводити в оману.
С.Лотт

3
BTW Короткий однокласинний валик можна згорнути за допомогою itertools.takewhileі itertools.count. Але це не краще, ніж drangeпродуктивність.
Кос

1
Прикра, що діапазон python цього не дозволяє, враховуючи, наскільки просто реалізувати генератор, який робить це навіть без накопичення помилок округлення. Чорт, навіть seqінструмент у GNU coreutils дозволяє обійтися seq 0 0.1 1без помилок округлення!
Джош

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

Для зручності пропозицію @ Коса можна реалізувати як ( itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))або itertools.islice(itertools.count(0,0.1), 10)після того, як у вас є import itertools), хоча я не перевіряв, що є більш ефективним
Анонімний

Відповіді:


905

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

Ви можете використовувати функцію простору з бібліотеки 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 - це такий всюдисущий компонент пітона, що я вважаю цю відповідь найбільш «пітонічною» з усіх.
авіаудар

21
@AndreTerra Проблема полягає в тому, що @ numpy @ є стороннім пакетом і додає багато накладних витрат з точки зору управління залежностями, зберігання (для самого пакету) тощо. Залежно від того, що робить розробник, використовувати його може бути неможливо це.
rbaleksandar

Вибачте мене, але я не зрозумів з плаваючою точкою помилки округлення в останній частині , так як np.linspace(1.,1.3,4)і np.arange(1.,1.3,0.1)дають такий самий результат
Deadcode

4
@deadcode Причиною є те, що np.arange визначений для створення діапазону [start,stop)(тобто виключення stop), тому не слід було очікувати, що до списку буде включено 1,3. Дивіться це питання, чому він все ще включений і що проти нього робити.
dennis

5
Скільки використовується пакет, мабуть, не є жодним показником того, чи є він "пітонічним".
Алекс Холл

213

Діапазон Python () може робити лише цілі числа, а не плаваючу крапку. У вашому конкретному випадку ви можете використовувати розуміння списку замість цього:

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

(Замініть виклик для діапазону цим виразом.)

Для більш загального випадку ви можете написати спеціальну функцію або генератор.


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:)
Блокувати

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
круглу функцію також можна використовувати lst = [круглий (x * 0,10,2) для x у діапазоні (0,10)]
БЕРЕЗЕНЬ

148

Спираючись на "xrange ([запуск], зупинка [, крок])" , ви можете визначити генератор, який приймає та виробляє будь-який обраний тип (дотримуйтесь типів, що підтримують +та <):

>>> 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
Крістіан Оудар

Я би трохи розширив його на інший напрямок (a r> стоп) і відповідний r - = крок для надання протилежного напрямку.
користувач318904

1
Я виконував функцію xfrange без проблем з точністю поплавця, згаданими вище. Перевірте;) stackoverflow.com/questions/477486/…
Carlos Vega

1
Ви накопичуєте помилки округлення. Будь ласка, використовуйте це замість: `i = 0; r = початок, поки r <стоп: i + = 1; r = старт + i * крок; врожайність r`
Cees Timmerman

1
Це з pythoncentral.io/pythons-range-function- пояснено (та інші джерела документації Python)
Апостолос

31

Збільште величину iпетлі, а потім зменшіть її, коли вам це потрібно.

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

EDIT: Я чесно не можу пригадати, чому я думав, що це буде працювати синтаксично

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

Це повинно мати бажаний вихід.


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

1
@cmsjr creative: D Тільки одна дрібниця: розділіть на 100.0, щоб Python не мав обробляти результат, якщо ви використовуєте Python 2.x. Я думаю, що в 3.0 він працюватиме так, як ви його зашифрували.
Дана

2
for i * 100 in range(0, 100, 10): SyntaxError: не вдається призначити оператору
Anne van Rossum

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
Це чудова відповідь для того, хто хоче отримати його, не надто сильно потрапляючи в пітон.
Чані

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: Приємний улов, дякую. Я думаю, що мені доведеться вирішити одне з більш складних рішень, щоб зберегти його загальним.
ДругFX

14

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

Я б сказав просто скористатися циклом:

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 до 10)
Deleteman

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, тому що я виявив, що в деяких випадках я хотів уникнути дрейфу округлення, введеного арифметикою з плаваючою комою.

Це відповідає порожнім заданим результатам, як у діапазоні / 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? Ця частина коду більше навіть не враховує розмір кроку.
Джош

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 () дозволяє робити кроки в плаву. Але він повертає нутризований масив, який для нашої зручності можна перетворити в список за допомогою 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з обчислення, це не точно float0.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.