Come utilizzare un valore di intervallo intervallo decimale ()?


744

C'è un modo per passare da 0 a 1 di 0,1?

Pensavo di poterlo fare come segue, ma non è riuscito:

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

Invece, dice che l'argomento passo non può essere zero, cosa che non mi aspettavo.


17
int (0.1) == 0, quindi il passaggio è effettivamente zero. Potrebbe essere inaspettato, ma è zero. Potresti voler riaffermare la tua domanda per riflettere quel fatto che non te l'aspettavi. Dire "non è" è falso e fuorviante.
S.Lott

3
A proposito, è possibile arrotolare un liner corto con itertools.takewhilee itertools.count. drangeTuttavia, non è meglio delle prestazioni.
Kos,

1
È imbarazzante che la gamma di Python non lo consenta, dato quanto è facile implementare un generatore che lo fa anche senza accumulare errori di arrotondamento. Diamine, anche lo seqstrumento nei coreutils GNU permette di fare a seq 0 0.1 1meno degli errori di arrotondamento!
Jos

3
@josch: sequtilizza long doubleinternamente il tipo C ed è soggetto ad errori di arrotondamento. Ad esempio sulla mia macchina, seq 0 0.1 1fornisce 1come ultimo output (come previsto), ma seq 1 0.1 2fornisce 1.9come ultimo output (anziché previsto 2).
Mark Dickinson,

Per comodità, il suggerimento di @ Kos può essere implementato come itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))o itertools.islice(itertools.count(0,0.1), 10)(dopo averlo fatto import itertools), anche se non ho testato quale sia più efficiente
Anonimo

Risposte:


906

Piuttosto che usare direttamente un passo decimale, è molto più sicuro esprimerlo in termini di quanti punti vuoi. In caso contrario, è probabile che l'errore di arrotondamento in virgola mobile dia un risultato errato.

È possibile utilizzare la funzione linspace dalla libreria NumPy (che non fa parte della libreria standard ma è relativamente facile da ottenere). linspacerichiede un numero di punti per restituire e consente inoltre di specificare se includere o meno l'endpoint corretto:

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

Se vuoi davvero usare un valore di passo in virgola mobile, puoi farlo con 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])

Errore di arrotondamento a virgola mobile sarà causare problemi, però. Ecco un semplice caso in cui l'errore di arrotondamento provoca la arangeproduzione di un array di lunghezza 4 quando dovrebbe produrre solo 3 numeri:

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

51
numpy è una componente così onnipresente di Python che considero questa risposta la più "pitonica" di tutte.
attacco aereo

21
@AndreTerra Il problema è che @ numpy @ è un pacchetto di terze parti e aggiunge un sacco di sovraccarico in termini di gestione delle dipendenze, archiviazione (per il pacchetto stesso) ecc. A seconda di ciò che lo sviluppatore sta facendo, potrebbe essere impossibile usare esso.
rbaleksandar,

Scusatemi, ma non ho capito l'errore di arrotondamento in virgola mobile nell'ultima parte da allora np.linspace(1.,1.3,4)e ho np.arange(1.,1.3,0.1)dato esattamente lo stesso risultato
deadcode

4
@deadcode Il motivo è che np.arange è definito per produrre un intervallo [start,stop)(cioè escludendo stop), quindi non ci si aspetterebbe che 1.3 fosse incluso nell'elenco. Vedi questa domanda per sapere perché è ancora inclusa e cosa fare contro di essa.
dennis,

5
Quanto sia usato un pacchetto non è probabilmente un indicatore del fatto che sia "Pythonic".
Alex Hall,

213

L'intervallo di Python () può fare solo numeri interi, non in virgola mobile. Nel tuo caso specifico, puoi invece utilizzare una comprensione dell'elenco:

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

(Sostituisci la chiamata per l'intervallo con quell'espressione.)

Per il caso più generale, potresti voler scrivere una funzione o un generatore personalizzati.


36
Ancora meglio, potresti semplicemente usare la comprensione di un generatore se lavori con Python 2.4+. (x * 0.1 for x in range(0, 10)).
JAB

42
Ancora meglio, metti x/10invece di x * 0.1: D Niente di speciale in realtà, ma alcuni numeri saranno più precisi, ad esempio per 3*0.1te 0.30000000000000004, mentre per 3/10 ottieni 0.3:)
Bloke,

4
3/10 mi dà 0, non 0.3. 3 / 10.0 indica 0.2999999999999999999. Python 2.6.

19
@LarsWirzenius: in Python 2.2+, from __future__ import division; 3/10restituisce 0.3. Questo comportamento è predefinito in Python 3.x.
Benjamin Hodgson

2
la funzione round può anche essere usata lst = [round (x * 0.10,2) per x nel range (0,10)]
MARK

148

Partendo da 'xrange ([start], stop [, step])' , puoi definire un generatore che accetta e produce qualsiasi tipo tu scelga (attenersi ai tipi supportanti +e <):

>>> 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
Questo ha problemi di arrotondamento. Si prega di guardare qui: code.activestate.com/recipes/66472
Christian Oudard,

Lo estenderei un po 'per l'altra direzione con un (mentre r> stop) e un corrispondente r - = step per dare la direzione opposta.
user318904

1
Ho eseguito una funzione xfrange senza i problemi di precisione float di cui sopra. Dai un'occhiata;) stackoverflow.com/questions/477486/…
Carlos Vega,

1
Stai accumulando errori di arrotondamento. Si prega di usare questo invece: `i = 0; r = inizio mentre r <stop: i + = 1; r = start + i * step; resa r`
Cees Timmerman l'

1
Questo è tratto da pythoncentral.io/pythons-range-function-explained (e altre fonti di documentazione di Python)
Apostolos

31

Aumenta la grandezza di iper il loop e poi riduci quando ne hai bisogno.

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

EDIT: Onestamente non riesco a ricordare perché pensassi che avrebbe funzionato sintatticamente

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

Dovrebbe avere l'output desiderato.


Penso che troverai che range () funziona al di fuori degli interi, nel qual caso questa sarebbe l'unica soluzione, usando almeno la stessa funzione.
Matthew Scharley,

1
@cmsjr creative: D Solo una piccola cosa: dividi per 100.0 per impedire a Python di troncare il risultato se stai usando Python 2.x. Penso che in 3.0, funzionerà come l'hai codificato.
Dana,

2
for i * 100 in range(0, 100, 10): Sintassi Errore: impossibile assegnare all'operatore
Anne van Rossum,

25

scipyha una funzione integrata arangeche generalizza il range()costruttore di Python per soddisfare le tue esigenze di gestione del float.

from scipy import arange


8
Questo è esattamente lo stesso arangeche puoi trovare in numpy: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangetornerà True.
iFreilicht,

from nump import arange as range
shrewmouse, il

24

NumPy è un po 'eccessivo, penso.

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

In generale, fare un passo 1/xavanti a yte lo farebbe

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/xprodotto meno rumore di arrotondamento durante il test).


18

Simile alla funzione di R seq , questa restituisce una sequenza in qualsiasi ordine dato il valore di passo corretto. L'ultimo valore è uguale al valore di arresto.

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

risultati

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
Questa è un'ottima risposta per qualcuno che vuole ottenerlo senza entrare troppo in Python.
Chani,

1
Era quasi quello che stavo cercando: nota che seq(0.5, 3.0)ritorna [0.5, 1.5, 2.5, 3.5]. Per evitare ultime entry essere fuori-di-gamma, sostituire n = int(round(...con n = int(floor(...la linea from math import floorin alto (sopra def seq(...).
FriendFX,

2
@FriendFX Non farlo! Se floorutilizzato, seq(0.2, 0.9, 0.1)non riuscirà a raggiungere l'endpoint corretto e tornerà[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin

@ user502144: bella cattura, grazie. Immagino di dover accontentarmi di una delle soluzioni più complesse per mantenerlo generale.
FriendFX,

14

La funzione built-in range () restituisce una sequenza di valori interi, temo, quindi non puoi usarla per fare un passo decimale.

Direi solo usare un ciclo while:

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

Se sei curioso, Python sta convertendo il tuo 0,1 in 0, motivo per cui ti sta dicendo che l'argomento non può essere zero.


2
Non farlo! Aggiungere .110 volte non equivale ad aggiungere 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
gatto

12

Ecco una soluzione che utilizza 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)

Esempio di utilizzo:

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

Per completezza, dovresti calcolare il valore assoluto per la variabile sample_count, in questo modo la tua funzione funzionerà anche per un inizio negativo (cioè da -10 a 10)
Deleteman

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

in Python 2.7x ti dà il risultato di:

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

ma se usi:

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

ti dà il desiderato:

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



5

E se lo fai spesso, potresti voler salvare l'elenco generato r

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

5

more_itertoolsè una libreria di terze parti che implementa uno numeric_rangestrumento:

import more_itertools as mit


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

Produzione

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Questo strumento funziona anche per Decimale Fraction.


4

Le mie versioni utilizzano la funzione di intervallo originale per creare indici moltiplicativi per il turno. Ciò consente la stessa sintassi della funzione di intervallo originale. Ho realizzato due versioni, una usando float e una usando Decimal, perché ho scoperto che in alcuni casi volevo evitare la deriva di arrotondamento introdotta dall'aritmetica in virgola mobile.

È coerente con i risultati di set vuoti come in range / xrange.

Il passaggio di un solo valore numerico a una delle funzioni restituirà l'output dell'intervallo standard al valore intero del limite del parametro di input (quindi se lo si fornisse 5.5, si restituirebbe l'intervallo (6)).

Modifica: il codice seguente è ora disponibile come pacchetto su 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)) # []

Come può frangefunzionare se lo stop è None? Quella parte del codice non considera nemmeno più la dimensione del passo.
Jos

1
@josch rangeha due firme :, range(stop)che assume un valore predefinito start=0, step=1e range(start, stop, step), dove non vengono fatte ipotesi. frangeriflette questo. Quando si utilizza la range(stop)firma, sia frangee drangeinizia a 0 e incrementando di 1, quindi il loro comportamento è identico al normale range(stop)comportamento di arresto arrotondato all'intero più vicino.
Nisan.

4

Questa è la mia soluzione per ottenere intervalli con passaggi mobili.
Utilizzando questa funzione non è necessario importare numpy né installarlo.
Sono abbastanza sicuro che potrebbe essere migliorato e ottimizzato. Sentiti libero di farlo e pubblicalo qui.

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)

L'output è:

[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
Si è verificato un errore con la mancanza dell'ultimo valore se si trova entro 1 passaggio dal valore di arresto. cioè xfrange (1,10,2) fa solo 1,3,5,7, mancando 9
Lobe

Per riferimento e altri lettori, si prega di confrontare questa implementazione con questo stackoverflow.com/a/477610/54964 . Questo non sembra avere grossi problemi di galleggiamento.
Léo Léopold Hertz

@carlosvega Puoi confermare perché Lobe ottiene il suo risultato?
Léo Léopold Hertz 준영

3

Per completezza della boutique, una soluzione funzionale:

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

Puoi usare questa funzione:

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

Non sembra funzionare correttamente, ad eslist(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Shai Coleman,

2

Il trucco per evitare il problema di arrotondamento consiste nell'utilizzare un numero separato per spostarsi nell'intervallo, che inizia e metà del passo prima dell'inizio .

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

In alternativa, numpy.arangepuò essere utilizzato.


2

Può essere fatto usando la libreria Numpy. La funzione arange () consente i passaggi in virgola mobile. Ma restituisce un array intorpidito che può essere convertito in elenco usando tolist () per nostra comodità.

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

2

La mia risposta è simile ad altri usando map (), senza la necessità di NumPy e senza usare lambda (anche se potresti). Per ottenere un elenco di valori float da 0,0 a t_max in passi di dt:

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

2

Sorpreso nessuno ha ancora menzionato la soluzione consigliata nei documenti di Python 3 :

Guarda anche:

  • La ricetta di linspace mostra come implementare una versione lenta della gamma adatta per applicazioni in virgola mobile.

Una volta definita, la ricetta è facile da usare e non richiede numpyné altre librerie esterne, ma funzioni simili numpy.linspace(). Si noti che anziché un stepargomento, il terzo numargomento specifica il numero di valori desiderati, ad esempio:

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

Cito di seguito una versione modificata della ricetta completa di Python 3 di Andrew Barnert:

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

Per contrastare i problemi di precisione del galleggiante, è possibile utilizzare il Decimalmodulo .

Ciò richiede un ulteriore sforzo di conversione Decimalda into floatdurante la scrittura del codice, ma è possibile invece passare stre modificare la funzione se tale tipo di praticità è effettivamente necessario.

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

Output di esempio -

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
Con Decimalinput simili , np.arangefunziona allo stesso modo:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj

2
Sì grazie. Tuttavia, ciò richiederebbe una lib esterna (intorpidita).
shad0w_wa1k3r

1

Aggiungi correzione automatica per la possibilità di un segno errato al passaggio:

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

La mia soluzione:

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

Migliore soluzione: nessun errore di arrotondamento
_________________________________________________________________________________

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

_________________________________________________________________________________

Oppure, per un intervallo impostato invece di punti dati impostati (ad es. Funzione continua), utilizzare:

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

Per implementare una funzione: sostituire x / pow(step, -1)con f( x / pow(step, -1) )e definire f.
Per esempio:

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

start e stop sono inclusivi anziché l'uno o l'altro (di solito lo stop è escluso) e senza importazioni e l'utilizzo di generatori

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 lug 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]


1

So che sono in ritardo alla festa qui, ma ecco una banale soluzione di generatore che funziona in 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

allora puoi chiamarlo proprio come l'originale range()... non c'è gestione degli errori, ma fammi sapere se c'è un errore che può essere ragionevolmente rilevato, e aggiornerò. oppure puoi aggiornarlo. questo è StackOverflow.


0

Ecco la mia soluzione che funziona perfettamente con float_range (-1, 0, 0,01) e funziona senza errori di rappresentazione in virgola mobile. Non è molto veloce, ma funziona bene:

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

Sono solo un principiante, ma ho avuto lo stesso problema durante la simulazione di alcuni calcoli. Ecco come ho tentato di risolverlo, che sembra funzionare con decimali.

Sono anche abbastanza pigro e quindi ho trovato difficile scrivere la mia funzione di intervallo.

Fondamentalmente quello che ho fatto è cambiato il mio xrange(0.0, 1.0, 0.01)per xrange(0, 100, 1)e utilizzato la divisione per 100.0all'interno del ciclo. Ero anche preoccupato, se ci saranno errori di arrotondamento. Quindi ho deciso di testare se ce ne sono. Ora ho sentito che se per esempio 0.01da un calcolo non è esattamente il float0.01 che li confronta dovrebbe restituire False (se sbaglio, per favore fatemi sapere).

Quindi ho deciso di testare se la mia soluzione funzionerà per la mia gamma eseguendo un breve test:

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

E ha stampato True per ciascuno.

Ora, se sbaglio totalmente, per favore fatemelo sapere.


0

Questa fodera non ingombra il tuo codice. Il segno del parametro step è importante.

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)]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.