# Generating equally-spaced floats with least rounding error

Stefan Krah stefan-usenet at bytereef.org
Sat Sep 24 22:30:45 CEST 2011

```Arnaud Delobelle <arnodel at gmail.com> wrote:
> >>>> start, stop, n = -1, 1.1, 7
> >>>> [float(F(start) + i*(F(stop)-F(start))/n) for i in range(n+1)]
> > [-1.0, -0.7, -0.39999999999999997, -0.09999999999999996,
> > 0.20000000000000004, 0.5000000000000001, 0.8, 1.1]
>
> >>> start, stop, n = -1, 1.1, 7
> >>> [((n-i)*start + i*stop)/n for i in range(n+1)]
> [-1.0, -0.7000000000000001, -0.39999999999999997,
> -0.09999999999999996, 0.20000000000000004, 0.5, 0.8, 1.1]
>
> On these examples, using fractions is no better than what I suggested
> in my previous post.

Why not use Decimal if one needs exact endpoints? Something like:

def drange(start, stop, step=1):
if (step == 0):
raise ValueError("step must be != 0")
with localcontext() as ctx:
ctx.traps[Inexact] = True
x = start
cmp = -1 if step > 0 else 1
while x.compare(stop) == cmp:
yield float(x)
x += step

>>> list(drange(Decimal(1), Decimal("3.1"), Decimal("0.3")))
[1.0, 1.3, 1.6, 1.9, 2.2, 2.5, 2.8]
>>> list(drange(Decimal(-1), Decimal("1.1"), Decimal("0.3")))
[-1.0, -0.7, -0.4, -0.1, 0.2, 0.5, 0.8]
>>> list(drange(Decimal(-1), Decimal("1.1"), Decimal("0.1823612873")))
[-1.0, -0.8176387127, -0.6352774254, -0.4529161381, -0.2705548508, -0.0881935635, 0.0941677238, 0.2765290111, 0.4588902984, 0.6412515857, 0.823612873, 1.0059741603]
>>> list(drange(Decimal(-1), Decimal("1.1"), Decimal(1)/3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in drange
File "/usr/lib/python3.2/decimal.py", line 1178, in __add__
ans = ans._fix(context)
File "/usr/lib/python3.2/decimal.py", line 1652, in _fix
context._raise_error(Inexact)
File "/usr/lib/python3.2/decimal.py", line 3836, in _raise_error
raise error(explanation)
decimal.Inexact: None

Stefan Krah

```