# Generating equally-spaced floats with least rounding error

Arnaud Delobelle arnodel at gmail.com
Sun Sep 25 09:36:23 CEST 2011

```On 24 September 2011 23:10, Terry Reedy <tjreedy at udel.edu> wrote:

> On 9/24/2011 10:18 AM, Arnaud Delobelle wrote:
>>      ((n-i)*a + i*b)/n for i in range(n+1)
>
>>>> ['%20.18f' % x for x in [((7-i)*0.0 + i*2.1)/7 for i in range(8)]]
> ['0.000000000000000000', '0.299999999999999989', '0.599999999999999978',
> '0.900000000000000133', '1.199999999999999956', '1.500000000000000000',
> '1.800000000000000266', '2.100000000000000089']
>
> In the two places where this disagrees with the previous result, I believe
> it is worse. The *0.0 adds nothing. You are still multiplying an inexactly
> represented 2.1 by increasingly large numbers. Even 7*2.1/7 is not exact!

Of course *0.0 adds nothing in this case.  I was suggesting a general
solution to the problem of partitioning the interval (a, b) into n
subintervals of equal length.  As for 7*2.1/7 it is exactly the same
as 21/10 to 18 decimal places as the output of your own solution shows
below.

[...]
> The best you can do for this example is
>>>> ['%20.18f' % (i/10 ) for i in range(0, 22, 3)]
> ['0.000000000000000000', '0.299999999999999989', '0.599999999999999978',
> '0.900000000000000022', '1.199999999999999956', '1.500000000000000000',
> '1.800000000000000044', '2.100000000000000089']

Can you give an implementation for any interval (a, b) where a, b are
floats and any partition size n where n is a positive int?

My suggestion was a simple answer which tries to avoid the obvious
pitfalls that the naive approaches may fall into, as shown

>>> def bary(start, stop, n):
...     return [((n-i)*start + i*stop)/n for i in range(n+1)]
...
>>> def width(start, stop, n):
...     width = stop - start
...     return [start + i*width/n for i in range(n+1)]
...

Here is where the 'width' approach will fail:

>>> width(-1.0, 1e-20, 4)
[-1.0, -0.75, -0.5, -0.25, 0.0]
>>> bary(-1.0, 1e-20, 4)
[-1.0, -0.75, -0.5, -0.25, 1e-20]

--
Arnaud

```