web-dev-qa-db-fra.com

Gamme avec pas de type flotteur

La documentation dit fondamentalement que range doit se comporter exactement comme cette implémentation (pour positif step):

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

Il dit également que ses arguments doivent être des entiers. Pourquoi donc? Cette définition n'est-elle pas également parfaitement valable si l'étape est un flotteur?

Dans mon cas, je suis esp. ayant besoin d'une fonction range qui accepte un type flottant comme argument step. Y en a-t-il dans Python ou dois-je implémenter le mien?


Plus spécifique: Comment pourrais-je traduire ce code C directement en Python d'une manière agréable (c'est-à-dire ne pas simplement le faire via une boucle while- manuellement)):

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

Vous pouvez utiliser numpy.arange .

EDIT: Les documents préfèrent numpy.linspaceMerci @Droogans d'avoir remarqué =)

51
Katriel

Une explication pourrait être les problèmes d'arrondi à virgule flottante. Par exemple, si vous pouviez appeler

range(0, 0.4, 0.1)

vous pourriez vous attendre à une sortie de

[0, 0.1, 0.2, 0.3]

mais vous obtenez en fait quelque chose comme

[0, 0.1, 0.2000000001, 0.3000000001]

en raison de problèmes d'arrondi. Et comme la plage est souvent utilisée pour générer des indices d'une certaine sorte, il s'agit uniquement d'entiers.

Pourtant, si vous voulez un générateur de portée pour les flotteurs, vous pouvez simplement lancer le vôtre.

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

Afin de pouvoir utiliser des nombres décimaux dans une expression de plage, une manière intéressante de le faire est la suivante: [x * 0,1 pour x dans la plage (0, 10)]

13
mariana soffer

Le problème avec la virgule flottante est que vous ne pouvez pas obtenir le même nombre d'éléments que prévu, en raison de l'inexactitude. Cela peut être un vrai problème si vous jouez avec des polynômes où le nombre exact d'éléments est assez important.

Ce que vous voulez vraiment, c'est une progression arithmétique; le code suivant fonctionnera très bien pour int, float et complex ... et les chaînes et les listes ...

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

Notez que ce code a de meilleures chances que votre dernière valeur se situe dans le rugissement d'un taureau de la valeur attendue que toute alternative qui maintient un total cumulé.

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

Correction: voici un gadget concurrentiel total cumulé :

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
>>>
6
John Machin

Lorsque vous ajoutez des nombres à virgule flottante, il y a souvent une petite erreur. Une range(0.0, 2.2, 1.1) renverrait-elle [0.0, 1.1] ou [0.0, 1.1, 2.199999999]? Il n'y a aucun moyen d'être certain sans une analyse rigoureuse.

Le code que vous avez publié est une solution de contournement correcte si vous en avez vraiment besoin. Soyez juste conscient des lacunes possibles.

5
Mark Ransom

Voici un cas particulier qui pourrait suffire:

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

Dans votre cas, ce serait:

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

et donc:

>>> [ .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]
3
ntg

Voici ce que j'utiliserais:

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

plutôt que:

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]

j'espère que ça aide.

1
Andrey