tc39 / proposal-iterator.range

A proposal for ECMAScript to add a built-in Iterator.range()
https://tc39.es/proposal-iterator.range/
MIT License
487 stars 13 forks source link

Precision lost (Number.MAX_VALUE + 20 === Number.MAX_VALUE) behavior #33

Closed Jack-Works closed 4 years ago

Jack-Works commented 4 years ago

What if the yielded number is bigger than Number.MAX_SAFE_INTEGER and start to lost precision?

Prior discussion: #7. Prior discussion is too long and mixed two cases of "dead loop" so I decided to split them into two issues.

There were many possible behaviors suggested in #7 for this situation. And by studying this behavior in other languages you can see they treat this very differently. I personally have no preference in this situation so I'm open to this and happy to see the consensus reached in some way.

Language Behavior
This proposal ❔ Not decided yet
Python ❎ Allowed, but fails len() etc. with OverflowError
Java ⚫ Emit nothing
Swift (Range) N/A, can't set to bigger than 9223372036854775807
Swift (StrideTo) ⚫ Emit nothing
Rust ♾ Endless loop (Production) / ❌ Panic (Debug)
Haskell ❌ Exception

(The full table at https://github.com/tc39/proposal-Number.range/blob/master/compare.md#too-big-overflow-behavior-for-int-type-bigint-like-range-not-included)

Andrew-Cottrell commented 4 years ago

I expect people deliberately using unusually large positive or negative numbers would be aware of the limitations of floating point and not be surprised by the output.

Future MDN documentation, and similar, might warn about the possibility of endless loops or precision loss when using unconstrained input values as either endpoint. The warning may recommend always ensuring input values are verified to be in a suitable interval.

tabatkins commented 4 years ago

The behavior we settled on was to set the value of each loop with a val = start + step*i;, precisely so that precision loss won't result in accidental infinite loops. We should just stick with that and let the behavior fall out of that: some iterations will have the same value, but it'll still advance until it passes the endpoint, at (approximately) (end-start)/step iterations.

(Approximately because if the endpoint can't be precisely represented and the step is less than 1ulp at the endpoint, it might take some more iterations to advance the value to the next representable value and finally exceed the endpoint.)

Nothing else needs to, or should, be done here, I think.

Jack-Works commented 4 years ago

Current behavior: stop iteration.

Number.range(0, Infinity, 1e308)

0 1e308 end

tabatkins commented 4 years ago

Yup, that's the behavior I'd expect for that situation.

Jack-Works commented 4 years ago

Hmm what if the step is very small, and the internal counter counts to infinity (or overflow to negative number?, according to the host implementation)?

Maybe add a isFinite / isNaN check on the internal counter...

tabatkins commented 4 years ago

This is another one of those situations where a "correctly working loop" won't terminate until after the heat death of the universe, so it's indistinguishable in practice from an infinite loop and you don't need to worry about it.

Jack-Works commented 4 years ago

Thanks, you're right. I'll mark it as resolved. If anyone has new concerns welcome to leave comments.