The documentation basically says that range
must behave exactly as this implementation (for positive step
):
def range(start, stop, step):
x = start
while True:
if x >= stop: return
yield x
x += step
It also says that its arguments must be integers. Why is that? Isn't that definition also perfectly valid if step is a float?
In my case, I am esp. needing a range
function which accepts a float type as its step
argument. Is there any in Python or do I need to implement my own?
More specific: How would I translate this C code directly to Python in a nice way (i.e. not just doing开发者_JS百科 it via a while
-loop manually):
for(float x = 0; x < 10; x += 0.5f) { /* ... */ }
You could use numpy.arange
.
EDIT: The docs prefer numpy.linspace
. Thanks @Droogans for noticing =)
One explanation might be floating point rounding issues. For example, if you could call
range(0, 0.4, 0.1)
you might expect an output of
[0, 0.1, 0.2, 0.3]
but you in fact get something like
[0, 0.1, 0.2000000001, 0.3000000001]
due to rounding issues. And since range is often used to generate indices of some sort, it's integers only.
Still, if you want a range generator for floats, you can just roll your own.
def xfrange(start, stop, step):
i = 0
while start + i * step < stop:
yield start + i * step
i += 1
In order to be able to use decimal numbers in a range expression a cool way for doing it is the following: [x * 0.1 for x in range(0, 10)]
The problem with floating point is that you may not get the same number of items as you expected, due to inaccuracy. This can be a real problem if you are playing with polynomials where the exact number of items is quite important.
What you really want is an arithmetic progression; the following code will work quite happily for int
, float
and complex
... and strings, and lists ...
def arithmetic_progression(start, step, length):
for i in xrange(length):
yield start + i * step
Note that this code stands a better chance of your last value being within a bull's roar of the expected value than any alternative which maintains a running total.
>>> 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: here's a 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
>>>
When you add floating point numbers together, there's often a little bit of error. Would a range(0.0, 2.2, 1.1)
return [0.0, 1.1]
or [0.0, 1.1, 2.199999999]
? There's no way to be certain without rigorous analysis.
The code you posted is an OK work-around if you really need this. Just be aware of the possible shortcomings.
Here is a special case that might be good enough:
[ (1.0/divStep)*x for x in range(start*divStep, stop*divStep)]
In your case this would be:
#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'
and so:
>>> [ .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]
This is what I would use:
numbers = [float(x)/10 for x in range(10)]
rather than:
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]
hope it helps.
Probably because you can't have part of an iterable. Also, floats
are imprecise.
精彩评论