Is there a range()
equivalent for floats in Python?
>>> range(0.5,5,1.5)
[0, 1, 2, 3, 4]
>>> range(0.5,5,0.5)
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
range(0.5,5,0.5)
ValueError: range() step argument must not be zero
You can either use:
or use lambda / map:
Why Is There No Floating Point Range Implementation In The Standard Library?
As made clear by all the posts here, there is no floating point version of
range()
. That said, the omission makes sense if we consider that therange()
function is often used as an index (and of course, that means an accessor) generator. So, when we callrange(0,40)
, we're in effect saying we want 40 values starting at 0, up to 40, but non-inclusive of 40 itself.When we consider that index generation is as much about the number of indices as it is their values, the use of a float implementation of
range()
in the standard library makes less sense. For example, if we called the functionfrange(0, 10, 0.25)
, we would expect both 0 and 10 to be included, but that would yield a vector with 41 values.Thus, an
frange()
function depending on its use will always exhibit counter intuitive behavior; it either has too many values as perceived from the indexing perspective or is not inclusive of a number that reasonably should be returned from the mathematical perspective.The Mathematical Use Case
With that said, as discussed,
numpy.linspace()
performs the generation with the mathematical perspective nicely:The Indexing Use Case
And for the indexing perspective, I've written a slightly different approach with some tricksy string magic that allows us to specify the number of decimal places.
Similarly, we can also use the built-in
round
function and specify the number of decimals:A Quick Comparison & Performance
Of course, given the above discussion, these functions have a fairly limited use case. Nonetheless, here's a quick comparison:
The results are identical for each:
And some timings:
Looks like the string formatting method wins by a hair on my system.
The Limitations
And finally, a demonstration of the point from the discussion above and one last limitation:
Further, when the
skip
parameter is not divisible by thestop
value, there can be a yawning gap given the latter issue:There are ways to address this issue, but at the end of the day, the best approach would probably be to just use Numpy.
A simpler library-less version
Aw, heck -- I'll toss in a simple library-less version. Feel free to improve on it[*]:
The core idea is that
nsteps
is the number of steps to get you from start to stop andrange(nsteps)
always emits integers so there's no loss of accuracy. The final step is to map [0..nsteps] linearly onto [start..stop].[*] In particular,
frange()
returns a list, not a generator. But it sufficed for my needs.