Generating equally-spaced floats with least rounding error
mwilson at the-wire.com
Sat Sep 24 10:26:08 EDT 2011
Steven D'Aprano wrote:
> I'm trying to generate a sequence of equally-spaced numbers between a
> lower and upper limit. Given arbitrary limits, what is the best way to
> generate a list of equally spaced floats with the least rounding error for
> each point?
> For example, suppose I want to divide the range 0 to 2.1 into 7 equal
> intervals, then the end-points of each interval are:
> and I'd like to return the values in the brackets. Using Decimal or
> Fraction is not an option, I must use floats. If the exact value isn't
> representable as a float, I'm okay with returning the nearest possible
> The width of each interval is:
> width = (2.1 - 0.0)/7
> The relevant points can be calculated in either of two methods:
> #1 add multiples of the width to the starting value, 0.
> #2 subtract multiples of width from the ending value, 2.1.
> (Repeated addition or subtraction should be avoided, as it increases the
> amount of rounding error.)
> Mathematically the two are equivalent, but due to rounding, they may not
> be. Here's a run using Python 3.2:
>>>> [0.0 + i*width for i in range(8)]
> [0.0, 0.3, 0.6, 0.8999999999999999, 1.2, 1.5, 1.7999999999999998, 2.1]
> The 4th and 7th values have rounding errors, the rest are exact.
>>>> [2.1 - (7-i)*width for i in range(8)]
> [0.0, 0.30000000000000027, 0.6000000000000001, 0.9000000000000001,
> 1.2000000000000002, 1.5, 1.8, 2.1]
> The 2nd, 3rd, 4th and 5th values have rounding errors. Note that the 7th
> value is exact here, but not above.
> Is there a way to pick between methods #1 and #2 (or some combination of
> the two) without human intervention so as to minimise the rounding error?
> Or is there some other way to generate equally-spaced floats? My efforts
> at googling have not been helpful.
When I've done this with ints (usually in small embedded systems) I've
low_limit + (total_width * i) / intervals
since it does the rounding on the biggest numbers where proportional error
will be least, and it's guaranteed to hit the low_limit and high_limit
exactly (as exactly as they can be represented, anyway.)
More information about the Python-list