[CLJ-1166] Range function accumulates minor errors when called on floating-point numbers Created: 19/Feb/13 Updated: 29/Mar/13 Resolved: 01/Mar/13
|Affects Version/s:||Release 1.5|
Due to range's incremental computation minor errors introduced by floating point arithmetic accumulate, becoming more noticeable in long ranges and causing unexpected behaviour.
Compare the output of the following:
=> (range 0.0 10.0 0.1)
=> (defn range' [start end step] (map #(+ start (* % step)) (range 0 (/ (- end start) step) 1)))
|Comment by Stephen Nelson [ 19/Feb/13 3:06 PM ]|
=> (last (range 0.0 10000000.0 0.1))
|Comment by Stuart Halloway [ 01/Mar/13 10:08 AM ]|
Range is incremental by design, and that is how floats work. Something with the desired behavior would need to be a different fn with a different name.
|Comment by Stephen Nelson [ 03/Mar/13 2:38 PM ]|
"Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step"
What specifically about that wording specifically suggests that the implementation will use naive increment-and-recurse behaviour? My reading is that the function will return a lazy sequence of numbers from start to end separated by step, not separated by 'almost step'.
This implementation leads to unexpected behaviour with bounds:
=> (count (range 0 100 1))
|Comment by Timothy Pratley [ 29/Mar/13 5:09 PM ]|
range could avoid this issue cleanly by converting floats to bigdecimals (let me know if you think this is a good idea?)
I ran into this problem recently, and have to say it was pretty ugly. This is how I avoided the issue:
Hope that helps any disillusioned float users out there, or just pass in BigDecimals to range instead of floats... I would go so far as to say using floats with range as it stands is almost always going to end in tears (or worse as Stephen describes ).
|Comment by Timothy Pratley [ 29/Mar/13 5:10 PM ]|
[and just to be clear if it is considered an error, it would be nice if range explicitly forbade it]