Wie verwende ich einen Dezimalbereich () Schrittwert?

744

Gibt es eine Möglichkeit, um 0,1 zwischen 0 und 1 zu wechseln?

Ich dachte, ich könnte es wie folgt machen, aber es schlug fehl:

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

Stattdessen heißt es, dass das Schrittargument nicht Null sein kann, was ich nicht erwartet hatte.

Evan Fosmark
quelle
17
int (0.1) == 0, also ist der Schritt tatsächlich Null. Es mag unerwartet sein, aber es ist Null. Vielleicht möchten Sie Ihre Frage wiederholen, um die Tatsache widerzuspiegeln, dass Sie dies nicht erwartet haben. "Es ist nicht" zu sagen ist falsch und irreführend.
S.Lott
3
Übrigens Ein kurzer Einzeiler kann mit itertools.takewhileund aufgerollt werden itertools.count. Es ist jedoch nicht besser als die drangeLeistung.
Kos
1
Es ist peinlich, dass die Reichweite von Python dies nicht zulässt, da es einfach ist, einen Generator zu implementieren, der dies auch ohne Anhäufung von Rundungsfehlern tut. Selbst mit dem seqTool in GNU Coreutils kann man auf seq 0 0.1 1Rundungsfehler verzichten!
Josch
3
@josch: seqverwendet den C - long doubleTypen intern und ist Gegenstand von Rundungsfehlern. Zum Beispiel auf meiner Maschine, seq 0 0.1 1gibt 1als letzte Ausgabe (wie erwartet), sondern seq 1 0.1 2gibt 1.9als letzte Ausgabe ( und nicht den erwarteten 2).
Mark Dickinson
Der Einfachheit halber kann der Vorschlag von @ Kos als itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))oder itertools.islice(itertools.count(0,0.1), 10)(nachdem Sie dies getan haben import itertools) implementiert werden , obwohl ich nicht getestet habe, was effizienter ist
Anonym

Antworten:

906

Anstatt einen Dezimalschritt direkt zu verwenden, ist es viel sicherer, dies in Bezug auf die Anzahl der gewünschten Punkte auszudrücken. Andernfalls führt ein Gleitkomma-Rundungsfehler wahrscheinlich zu einem falschen Ergebnis.

Sie können die Linspace- Funktion aus der NumPy- Bibliothek verwenden (die nicht Teil der Standardbibliothek ist, aber relativ einfach zu erhalten ist). linspaceFür die Rückgabe sind mehrere Punkte erforderlich. Außerdem können Sie angeben, ob der richtige Endpunkt eingeschlossen werden soll oder nicht:

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

Wenn Sie wirklich einen Gleitkomma-Schrittwert verwenden möchten, können Sie mit 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])

Gleitkommazahlen Rundungsfehler wird zu Problemen führen, wenn. Hier ist ein einfacher Fall, in dem Rundungsfehler dazu führen arange, dass ein Array mit einer Länge von 4 erzeugt wird, wenn nur 3 Zahlen erzeugt werden sollen:

>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])
Andrew Jaffe
quelle
51
Numpy ist eine so allgegenwärtige Komponente von Python, dass ich diese Antwort als die "pythonischste" von allen betrachte.
Luftangriff
21
@AndreTerra Das Problem ist, dass @ numpy @ ein Paket eines Drittanbieters ist und viel Aufwand in Bezug auf Abhängigkeitsverwaltung, Speicher (für das Paket selbst) usw. verursacht. Je nachdem, was der Entwickler tut, kann die Verwendung möglicherweise unmöglich sein es.
Rbaleksandar
Verzeihen Sie mir, aber ich habe den Gleitkomma-Rundungsfehler im letzten Teil seitdem nicht verstanden np.linspace(1.,1.3,4)und np.arange(1.,1.3,0.1)gebe genau die gleiche Ausgabe
Deadcode
4
@deadcode Der Grund dafür ist, dass np.arange so definiert ist, dass es einen Bereich erzeugt [start,stop)(dh ausschließt stop), sodass man nicht erwarten würde, dass 1.3 in die Liste aufgenommen wird. In dieser Frage erfahren Sie, warum sie noch enthalten ist und was Sie dagegen tun können.
Dennis
5
Wie oft ein Paket verwendet wird, ist wohl kein Indikator dafür, ob es "Pythonic" ist.
Alex Hall
213

Pythons range () kann nur Ganzzahlen ausführen, keine Gleitkommazahlen. In Ihrem speziellen Fall können Sie stattdessen ein Listenverständnis verwenden:

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

(Ersetzen Sie den Aufruf zum Bereich durch diesen Ausdruck.)

Für den allgemeineren Fall möchten Sie möglicherweise eine benutzerdefinierte Funktion oder einen benutzerdefinierten Generator schreiben.


quelle
36
Noch besser ist, wenn Sie mit Python 2.4+ arbeiten, können Sie einfach ein Generatorverständnis verwenden. (x * 0.1 for x in range(0, 10)).
JAB
42
Noch besser, setzen Sie x/10statt x * 0.1: D Eigentlich nichts Besonderes, aber einige Zahlen dort werden präziser sein, z. B. für 3*0.1Sie 0.30000000000000004, während Sie für 3/10 erhalten 0.3:)
Kerl
4
3/10 gibt mir 0, nicht 0,3. 3 / 10.0 ergibt 0,2999999999999999999. Python 2.6.
19
@LarsWirzenius: Gibt in Python 2.2+ from __future__ import division; 3/100.3 zurück. Dieses Verhalten ist die Standardeinstellung in Python 3.x.
Benjamin Hodgson
2
runde Funktion kann auch verwendet werden lst = [rund (x * 0,10,2) für x im Bereich (0,10)]
MARK
148

Aufbauend auf 'xrange ([start], stop [, step])' können Sie einen Generator definieren, der jeden von Ihnen gewählten Typ akzeptiert und erzeugt (halten Sie sich an unterstützende Typen +und <):

>>> 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']
>>> 
gimel
quelle
45
Dies hat Rundungsprobleme. Bitte schauen Sie hier: code.activestate.com/recipes/66472
Christian Oudard
Ich würde es ein bisschen für die andere Richtung mit einem (während r> stop) und einem entsprechenden r - = Schritt erweitern, um die entgegengesetzte Richtung zu geben.
user318904
1
Ich habe eine xfrange-Funktion ohne die oben genannten Probleme mit der Float-Präzision ausgeführt. Schau es dir an
Carlos Vega
1
Sie sammeln Rundungsfehler. Bitte verwenden Sie stattdessen Folgendes: `i = 0; r = Start, während r <Stopp: i + = 1; r = Start + i * Schritt; Ausbeute r`
Cees Timmerman
1
Dies ist aus pythoncentral.io/pythons-range-function-explained (und anderen Python-Dokumentationsquellen)
Apostolos
31

Erhöhen Sie die Größe ider Schleife und verringern Sie sie dann, wenn Sie sie benötigen.

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

EDIT: Ich kann mich ehrlich gesagt nicht erinnern, warum ich dachte, dass das syntaktisch funktionieren würde

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

Das sollte die gewünschte Leistung haben.

cmsjr
quelle
Ich denke, Sie werden feststellen, dass range () aus ganzen Zahlen besteht. In diesem Fall wäre dies die einzige Lösung, die mindestens dieselbe Funktion verwendet.
Matthew Scharley
1
@cmsjr creative: D Nur eine Kleinigkeit: Teilen Sie durch 100.0, um zu verhindern, dass Python das Ergebnis abschneidet, wenn Sie Python 2.x verwenden. Ich denke in 3.0 wird es funktionieren, wie Sie es codiert haben.
Dana
2
for i * 100 in range(0, 100, 10): SyntaxError: kann nicht dem Operator zugewiesen werden
Anne van Rossum
25

scipyverfügt über eine integrierte Funktion, arangedie den Python- range()Konstruktor verallgemeinert , um Ihre Anforderungen an die Float-Behandlung zu erfüllen.

from scipy import arange

Catherine Ray
quelle
8
Dies ist genau das gleiche arange, was Sie in numpy finden können: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangewird zurückkehren True.
iFreilicht
from nump import arange as range
Spitzmaus
24

NumPy ist ein bisschen übertrieben, denke ich.

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

Im Allgemeinen würde es genügen, einen Schritt nach 1/xoben zu ymachen

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/xerzeugte weniger Rundungsgeräusche, als ich testete).

Kalle
quelle
18

Ähnlich wie bei der seq Funktion von R gibt diese eine Sequenz in beliebiger Reihenfolge mit dem richtigen Schrittwert zurück. Der letzte Wert entspricht dem Stoppwert.

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

Ergebnisse

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]

Zeferino
quelle
2
Dies ist eine großartige Antwort für jemanden, der sie erhalten möchte, ohne sich zu sehr mit Python zu beschäftigen.
Chani
1
Das war fast das, wonach ich gesucht habe - beachten Sie, dass es seq(0.5, 3.0)zurückkehrt [0.5, 1.5, 2.5, 3.5]. Um zu vermeiden , letzte Einträge sind out-of-Bereich, ersetzen Sie n = int(round(...mit n = int(floor(...der Linie from math import flooran der Spitze ( siehe oben def seq(...).
FriendFX
2
@FriendFX Tu das nicht! Wenn floores verwendet wird, erreicht seq(0.2, 0.9, 0.1)es nicht den richtigen Endpunkt und kehrt zurück[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin
@ user502144: Netter Fang, danke. Ich muss mich wohl mit einer der komplexeren Lösungen zufrieden geben, um sie allgemein zu halten.
FriendFX
14

Die integrierte Funktion range () gibt leider eine Folge von ganzzahligen Werten zurück, sodass Sie sie nicht für einen Dezimalschritt verwenden können.

Ich würde sagen, benutze einfach eine while-Schleife:

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

Wenn Sie neugierig sind, konvertiert Python Ihre 0.1 in 0, weshalb es Ihnen sagt, dass das Argument nicht Null sein kann.

Dana
quelle
2
Tu das nicht! .110- maliges Hinzufügen ist nicht dasselbe wie Hinzufügen 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Katze
12

Hier ist eine Lösung mit 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)

Anwendungsbeispiel:

for i in seq(0, 1, 0.1):
    print(i)
Pramod
quelle
Der Vollständigkeit halber sollten Sie den absoluten Wert für die Variable sample_count berechnen. Auf diese Weise funktioniert Ihre Funktion auch für einen negativen Start (dh von -10 bis 10)
Deleteman
10
[x * 0.1 for x in range(0, 10)] 

In Python 2.7x erhalten Sie das Ergebnis von:

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

aber wenn Sie verwenden:

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

gibt Ihnen das gewünschte:

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

Nik
quelle
5

Wenn Sie dies häufig tun, möchten Sie möglicherweise die generierte Liste speichern r

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

more_itertoolsist eine Bibliothek eines Drittanbieters, die ein numeric_rangeTool implementiert :

import more_itertools as mit


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

Ausgabe

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Dieses Tool funktioniert auch für Decimalund Fraction.

Pylang
quelle
4

Meine Versionen verwenden die ursprüngliche Bereichsfunktion, um multiplikative Indizes für die Verschiebung zu erstellen. Dies ermöglicht die gleiche Syntax wie die ursprüngliche Bereichsfunktion. Ich habe zwei Versionen erstellt, eine mit float und eine mit Decimal, weil ich festgestellt habe, dass ich in einigen Fällen die durch die Gleitkomma-Arithmetik verursachte Rundungsdrift vermeiden wollte.

Dies stimmt mit den Ergebnissen der leeren Menge wie in range / xrange überein.

Wenn Sie nur einen einzigen numerischen Wert an eine der beiden Funktionen übergeben, wird die Standardbereichsausgabe auf den ganzzahligen Höchstwert des Eingabeparameters zurückgesetzt. Wenn Sie also 5,5 angeben, wird der Bereich (6) zurückgegeben.)

Bearbeiten: Der folgende Code ist jetzt als Paket auf pypi: Franges verfügbar

## 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)) # []
Nisan.H
quelle
Wie kann frangefunktionieren, wenn Stopp ist None? Dieser Teil des Codes berücksichtigt nicht einmal mehr die Schrittgröße.
Josch
1
@josch rangehat zwei Signaturen : range(stop), die einen Standard annehmen start=0, step=1, und range(start, stop, step)bei denen keine Annahmen getroffen werden. frangespiegelt das wider. Bei Verwendung der range(stop)Unterschrift, die beide frangeund drangebei 0 und Schrittweite von 1 beginnen, so dass ihr Verhalten regelmäßig identisch ist range(stop)Verhalten mit Stop gerundet auf die nächste ganze Zahl auf.
Nisan.H
4

Dies ist meine Lösung, um Bereiche mit Float-Schritten zu erhalten.
Mit dieser Funktion ist es nicht erforderlich, numpy zu importieren oder zu installieren.
Ich bin mir ziemlich sicher, dass es verbessert und optimiert werden könnte. Fühlen Sie sich frei, es zu tun und es hier zu posten.

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)

Die Ausgabe ist:

[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]
Carlos Vega
quelle
1
Beim Fehlen des letzten Werts liegt ein Fehler vor, wenn er innerhalb eines Schritts des Stoppwerts liegt. dh xfrange (1,10,2) macht nur 1,3,5,7, fehlt 9
Lobe
Vergleichen Sie diese Implementierung als Referenz und für andere Leser mit diesem stackoverflow.com/a/477610/54964 . Dies scheint keine großen Probleme mit dem Schwimmer zu haben.
Léo Léopold Hertz 준영
@carlosvega Kannst du bestätigen, warum Lobe sein Ergebnis bekommt?
Léo Léopold Hertz 12
3

Für die Vollständigkeit der Boutique eine funktionale Lösung:

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)
Bijou Trouvaille
quelle
2

Sie können diese Funktion verwenden:

def frange(start,end,step):
    return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))
user376536
quelle
Scheint nicht richtig zu funktionieren, zBlist(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Shai Coleman
2

Der Trick zu vermeiden Abrundungs Problem ist eine eigene Nummer zu verwenden , durch den Bereich zu bewegen, das beginnt und die Hälfte des Schritt vor Beginn .

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

Alternativ numpy.arangekann verwendet werden.

wolfram77
quelle
2

Dies kann mit der Numpy-Bibliothek erfolgen. Die Funktion arange () ermöglicht Schritte in float. Es wird jedoch ein numpy-Array zurückgegeben, das zur Vereinfachung mit tolist () in list konvertiert werden kann.

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

Meine Antwort ist ähnlich wie bei anderen, die map () verwenden, ohne NumPy zu benötigen und ohne Lambda zu verwenden (obwohl Sie dies könnten). So erhalten Sie eine Liste von Gleitkommawerten von 0,0 bis t_max in Schritten von dt:

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

Überrascht hat noch niemand die empfohlene Lösung in den Python 3-Dokumenten erwähnt :

Siehe auch:

  • Das Linspace-Rezept zeigt, wie eine verzögerte Version des Bereichs implementiert wird, die für Gleitkommaanwendungen geeignet ist.

Einmal definiert, ist das Rezept einfach zu verwenden und erfordert keine numpyoder andere externe Bibliotheken, funktioniert aber wie numpy.linspace(). Beachten Sie, dass stepdas dritte numArgument anstelle eines Arguments die Anzahl der gewünschten Werte angibt, zum Beispiel:

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

Ich zitiere eine modifizierte Version des vollständigen Python 3-Rezepts von Andrew Barnert unten:

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))
Chris_Rands
quelle
2

Um den Problemen mit der Float-Genauigkeit entgegenzuwirken, können Sie das DecimalModul verwenden .

Dies erfordert einen zusätzlichen Aufwand für die Konvertierung Decimalvon intoder floatwährend des Schreibens des Codes. Sie können jedoch stattdessen strdie Funktion übergeben und ändern, wenn diese Art von Komfort tatsächlich erforderlich ist.

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

Beispielausgaben -

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')]
shad0w_wa1k3r
quelle
2
Mit ähnlichen DecimalEingaben np.arangefunktioniert das gleiche:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj
2
Ja, danke. Dies würde allerdings eine externe (numpy) lib erfordern.
Shad0w_wa1k3r
1

Fügen Sie eine automatische Korrektur für die Möglichkeit eines falschen Anmeldeschritts hinzu:

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

Meine Lösung:

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

Beste Lösung: kein Rundungsfehler
_________________________________________________________________________________

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

_________________________________________________________________________________

Oder verwenden Sie für einen festgelegten Bereich anstelle von festgelegten Datenpunkten (z. B. kontinuierliche Funktion):

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

So implementieren Sie eine Funktion: Ersetzen x / pow(step, -1)durch f( x / pow(step, -1) )und definieren f.
Zum Beispiel:

>>> 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]
Jason
quelle
1

Start und Stopp sind inklusive und nicht der eine oder andere (normalerweise ist Stopp ausgeschlossen) und ohne Importe und unter Verwendung von Generatoren

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

Goran B.
quelle
1

Ich weiß, dass ich zu spät zur Party komme, aber hier ist eine triviale Generatorlösung, die in 3.6 funktioniert:

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

dann können Sie es wie das Original aufrufen range()... es gibt keine Fehlerbehandlung, aber lassen Sie mich wissen, wenn es einen Fehler gibt, der vernünftigerweise abgefangen werden kann, und ich werde ihn aktualisieren. oder Sie können es aktualisieren. Dies ist StackOverflow.

David Culbreth
quelle
0

Hier ist meine Lösung, die gut mit float_range (-1, 0, 0.01) funktioniert und ohne Gleitkomma-Darstellungsfehler funktioniert. Es ist nicht sehr schnell, funktioniert aber gut:

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]
Pymen
quelle
0

Ich bin nur ein Anfänger, aber ich hatte das gleiche Problem, als ich einige Berechnungen simulierte. Hier ist, wie ich versucht habe, dies herauszufinden, was mit Dezimalschritten zu funktionieren scheint.

Ich bin auch ziemlich faul und fand es schwierig, meine eigene Bereichsfunktion zu schreiben.

Im Grunde , was ich getan habe ist meine geändert xrange(0.0, 1.0, 0.01)zu xrange(0, 100, 1)und verwendet , um die Division durch die 100.0innerhalb der Schleife. Ich war auch besorgt, ob es Rundungsfehler geben wird. Also habe ich mich entschlossen zu testen, ob es welche gibt. Jetzt habe ich gehört, dass wenn zum Beispiel 0.01aus einer Berechnung nicht genau der Float0.01 , der sie vergleicht, False zurückgegeben werden sollte (wenn ich falsch liege, lassen Sie es mich bitte wissen).

Deshalb habe ich mich entschlossen zu testen, ob meine Lösung für mein Sortiment funktioniert, indem ich einen kurzen Test durchführte:

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

Und es druckte True für jeden.

Wenn ich es jetzt völlig falsch verstehe, lass es mich wissen.

user2836437
quelle
0

Dieser eine Liner wird Ihren Code nicht überladen. Das Vorzeichen des Schrittparameters ist wichtig.

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