Generating a list of equally-spaced floats can surprising due to floating point rounding. See, for example, the recipe for a floating point range. One way of avoiding some surprises is by changing the API: instead of specifying a start, stop and step values, instead use a start, stop and count:

```
>>> list(spread(0.0, 2.1, 7))
[0.0, 0.3, 0.6, 0.9, 1.2, 1.5, 1.8]
```

Like frange `spread`

takes an optional mode argument to select whether the start and end values are included. By default, start is included and end is not, and exactly count values are returned.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | ```
from fractions import Fraction
def spread(start, end, count, mode=1):
"""spread(start, end, count [, mode]) -> generator
Yield a sequence of evenly-spaced numbers between start and end.
The range start...end is divided into count evenly-spaced (or as close to
evenly-spaced as possible) intervals. The end-points of each interval are
then yielded, optionally including or excluding start and end themselves.
By default, start is included and end is excluded.
For example, with start=0, end=2.1 and count=3, the range is divided into
three intervals:
(0.0)-----(0.7)-----(1.4)-----(2.1)
resulting in:
>>> list(spread(0.0, 2.1, 3))
[0.0, 0.7, 1.4]
Optional argument mode controls whether spread() includes the start and
end values. mode must be an int. Bit zero of mode controls whether start
is included (on) or excluded (off); bit one does the same for end. Hence:
0 -> open interval (start and end both excluded)
1 -> half-open (start included, end excluded)
2 -> half open (start excluded, end included)
3 -> closed (start and end both included)
By default, mode=1 and only start is included in the output.
(Note: depending on mode, the number of values returned can be count,
count-1 or count+1.)
"""
if not isinstance(mode, int):
raise TypeError('mode must be an int')
if count != int(count):
raise ValueError('count must be an integer')
if count <= 0:
raise ValueError('count must be positive')
if mode & 1:
yield start
width = Fraction(end-start)
start = Fraction(start)
for i in range(1, count):
yield float(start + i*width/count)
if mode & 2:
yield end
``` |

One subtlety is that count is not the number of points returned, but the number of divisions in the range. The number of points yielded will be count-1 if you exclude both the start and end points, and count+1 if you include them both. This is just the usual fence-post problem, although in this case it should not be considered an error.

Another solution to this problem, although one with a different API, is linspace from `numpy`

. But note that it too can be surprising (although not as surprising as `frange`

). Results may look exact when printed, but may not necessarily be the exact value you expect:

```
>>> import numpy as np # (using Python 2.5)
>>> np.linspace(0.0, 2.1, 8, True)
array([ 0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1])
>>>
>>> exact = (0.0, 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1)
>>> [a-b for (a,b) in zip(np.linspace(0.0, 2.1, 8, True), exact)]
[0.0, 0.0, 0.0, -1.1102230246251565e-16, 0.0, 0.0, -2.2204460492503131e-16, 0.0]
```

In this case, `spread`

manages to do slightly better:

```
>>> [a-b for (a,b) in zip(spread(0.0, 2.1, 7, mode=3), exact)]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
```

although that will not necessarily always be the case.