# Why Number.range instead of a new object like Range?

**URL:** <https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979>\
**Category:** I have questions\
**Created:** [September 8, 2021, 5:32pm UTC](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979 "2021-09-08T17:32:43Z")\
**Posts on this page:** 5\
**Page:** 2

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [September 10, 2021, 11:17pm UTC](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979/21 "2021-09-10T23:17:13Z")

</div>

I think you missed my point - I was going for exact `===` precision, which is a bit different than "almost exact".

---

<div class="post-metadata">

**Author:** ![lightmare](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/lightmare/32/843_2.png) [@lightmare](https://es.discourse.group/u/lightmare)\
**Post date:** [September 10, 2021, 11:42pm UTC](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979/22 "2021-09-10T23:42:24Z")

</div>

There is no such thing as exact `0.1` or exact `0.3` in binary float.

`0.1` is rounded to `S := 3602879701896397 / 36028797018963968`  
`0.3` is rounded to `A := 5404319552844595 / 18014398509481984`  
`0.30000000000000004` is rounded to `B := 1351079888211149 / 4503599627370496`

For the 4th step of `Range(0, 1, 0.1)`:  
`R := 3 * S == 10808639105689191 / 36028797018963968`.

`R` needs one more bit than would fit into float64, and lies halfway between `A` and `B`, thus both are equally correct roundings of `R`.

In other words, `0.3` is not better than `0.30000000000000004`, they are both half ulp off, so why would you spend extra effort meddling with it?

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [September 10, 2021, 11:52pm UTC](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979/23 "2021-09-10T23:52:52Z")

</div>

Just to be clear, I'm just offering solutions to @tabatkins' complaints. I personally disagree about it even being important - [you shouldn't be using floats like that anyways](https://floating-point-gui.de/).

---

<div class="post-metadata">

**Author:** ![jithujoshyjy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jithujoshyjy/32/399_2.png) [@jithujoshyjy](https://es.discourse.group/u/jithujoshyjy)\
**Post date:** [September 14, 2021, 12:33pm UTC](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979/24 "2021-09-14T12:33:27Z")

</div>

Guess I'm a little late to comment here😅. From your question: **Why Number.range instead of a new object like Range?**  
The Range object already exists as part of the DOM api. It's used to represent dom nodes. So the name is already taken😉

---

<div class="post-metadata">

**Author:** ![JackWorks](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jackworks/32/260_2.png) [@JackWorks](https://es.discourse.group/u/JackWorks)\
**Post date:** [September 15, 2021, 5:05am UTC](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979/25 "2021-09-15T05:05:09Z")

</div>

I'm a bit late here, but I'd like to explain why I made all those decisions.

cc @NickGard

> 1. Why split constructors for BigInt and Number ranges? (Number.range / BigInt.range)  
> a. Is there a reason that Range(\<number|bigint\>, \<number|bigint\>) isn't a good idea as long as the types match?  
> b. None of the other methods on the Number object return new types. They either return a number (e.g. parseInt) or a primitive (e.g. isFinite, toLocaleString)

1. It might be possible to have a `String.range` or `Temporal.range` in the future. Mix all of them in a `Range` will cause confusion when there is a type mismatch.
2. Mixing number and bigint is somehow convenient for cases like `Number.range(0, 10n)`. I think it should work. I have opened an issue in the proposal repo. [Mixing Number and Bigint · Issue #49 · tc39/proposal-iterator.range · GitHub](https://github.com/tc39/proposal-Number.range/issues/49)
3. Unfortunately, the `Range` is a global API on the Web. There was also an issue for this: [Consider first-class type (i.e. 'NumberRange' or 'Interval') vs. a 'range' method · Issue #22 · tc39/proposal-iterator.range · GitHub](https://github.com/tc39/proposal-Number.range/issues/22)
4. About 1.b, I don't think it's a strong conversion in the past API designs so I guess it's ok to do that.

> 1. Why return an iterator instead of an iterable?

Yes, this is a serious problem that is discussed at [https://github.com/tc39/proposal-Number.range/issues/17](https://github.com/tc39/proposal-Number.range/issues/17). I want to convert to iterable but many others don't agree, I think we are stuck here.

There is some research of this by @yulia on this topic.

> 1. The proposal explicitly calls out `if (x in Range(0, 30))` as a non-goal, but this seems like a common use-case of ranges in other languages.

To support this operation, we need to overwrite `[[has]]`, this make the range object no longer a normal object but a extoic object.

Furthermore, in the JavaScript history we don't have this pattern, for example, `"a" in "ab"` results in TypeError.

So I don't think it should be included.

And as @tabatkins said, it's really subtle to decide which semantics to use. Should 5 be in the range of -10 to 10 with step 10? (And more problem with floating-point numbers).

> 1. Why return undefined for bad inputs rather than an empty iterable?

Actually, we throw TypeError for bad input. There is a playground you can try it [https://tc39.es/proposal-Number.range/playground.html](https://tc39.es/proposal-Number.range/playground.html).

> 1. Why store start, end, step, and inclusiveEnd on the range? No other iterator/iterable stores metadata like this.

This is designed for the future. For example, imagine we add a `contains()` method in the future, with those metadata, polyfill authors can simply add this `contains()` to the prototype.

If we don't expose these, they will need to overwrite the `range` method itself to collect the initial parameters and re-implement the normalization steps to the parameters in the specification.

> What does the current proposal/polyfill do for 2^53?

Good question, I don't know 😂

* * *

Reply to @tabatkins:

> 2b) If you need a reusable Range, we already have a way to do that - ()=\>Range(1,5). Each invocation will produce an iterator.

Yeah, but as @NickGard said,

> a. Unexpectedly stateful objects like iterators have been a footgun in the past. For instance, RegExp objects with the global flag maintain state and the rule of thumb is to create them as needed instead of reusing them (e.g. `if (/foo/g.test(str))` instead of `const FOO = /foo/g; /* ...later */ if (FOO.test(str))` .)

I agree with this argument. It might be surprising.

[Previous page](https://es.discourse.group/t/why-number-range-instead-of-a-new-object-like-range/979.md?page=1)
