Questions on clz and toInteger

# Luke Hoban (12 years ago)

Two questions on new Number APIs:

  1. Is it intentional that clz is on Number.prototype instead of Number? Why?
  2. Is it intentional that Number.toInteger(Infinity) returns true?
# Allen Wirfs-Brock (12 years ago)

On Jul 11, 2013, at 9:01 PM, Luke Hoban wrote:

Two questions on new Number APIs:

  1. Is it intentional that clz is on Number.prototype instead of Number? Why?

Generally, operations that operate upon a value of a specific type are expressed as instance methods. We see this all the time with regular objects but we also we see this in String where we have many methods that operate upon string values. For Number we have the toFixed, etc. methods that convert number values to strings. To me, clz seem like it fall into this category of method that operate upon instances of a specific type. For example, compare and contrast:

'00101000111100001111000011110000'.indexOf('1') // 2
0x28f0f0f0.clz()   //2

both use instance methods, applied to a primitive value, that report something about the structure of the value

The (new) is* methods on the Number constructor are generally different in that they are predicates that test values that may not actually be numbers so they can't be Number.prototype methods.

I think there is a stronger case to me made for Math.clz(number). Number.prototype and Math both seem like plausible homes for clz. In the end, I placed it on Number prototype because it is an operation that is specific to a particular numeric encoding rather than an implementation of a general mathematical function.

  1. Is it intentional that Number.toInteger(Infinity) returns true?

Huh? How's that?

Number.toInteger is specified as the single step:

1 Return ToInteger(number)

and step 4 of the abstract operation ToInteger(number):

4 If number is +0, -0, +∞, or -∞, return number.

# Mark S. Miller (12 years ago)

On Fri, Jul 12, 2013 at 8:49 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

  1. Is it intentional that Number.toInteger(Infinity) returns true?

Huh? How's that?

Number.toInteger is specified as the single step:

1 Return ToInteger(number)

and step 4 of the abstract operation ToInteger(number):

4 If number is +0, -0, +∞, or -∞, return number.

I must have missed that when it came up. I find it bizarre that toInteger(anything) === Infinity. Infinity is not an integer.

# Luke Hoban (12 years ago)

From: Allen Wirfs-Brock [mailto:allen at wirfs-brock.com]

On Jul 11, 2013, at 9:01 PM, Luke Hoban wrote:

Two questions on new Number APIs:

  1. Is it intentional that clz is on Number.prototype instead of Number? Why?

I think there is a stronger case to me made for Math.clz(number). Number.prototype and Math both seem like plausible homes for clz. In the end, I placed it on Number prototype because it is an operation that is specific to a particular numeric encoding rather than an implementation of a general mathematical function.

Yeah, I think Math would have been less surprising. I don't feel strongly, but Number.prototype wasn't what I had expected.

  1. Is it intentional that Number.toInteger(Infinity) returns true? Huh? How's that?

Number.toInteger is specified as the single step:

1 Return ToInteger(number)

and step 4 of the abstract operation ToInteger(number):

4 If number is +0, -0, +∞, or -∞, return number.

Sorry, I meant isInteger. Per your quoted section, toInteger(Infinity) is Infinity, so isInteger(Infinity) is true.

# Mark Miller (12 years ago)

On Fri, Jul 12, 2013 at 8:58 AM, Luke Hoban <lukeh at microsoft.com> wrote:

Sorry, I meant isInteger. Per your quoted section, toInteger(Infinity) is Infinity, so isInteger(Infinity) is true.

And I find that equally bizarre and unpleasant.

# Allen Wirfs-Brock (12 years ago)

On Jul 12, 2013, at 9:01 AM, Mark Miller wrote:

And I find that equally bizarre and unpleasant.

ToInteger is an internal operation. Algorithms that call it can explicitly check for infinities if it is important to them. However, a quick scan of its current uses shows that passing through infinities is exactly want is needed in most cases.

Number.toInteger is a language level function and it is certainly reasonable to explicitly discuss what it should return for infinities (and for that matter NaN and -0). So what do you think it should do for those cases?

Also, the current Math.isInteger spec. is just plan bogus:

3.   If integer is not equal to number, return false.

What (in the spec.) does "not equal" mean? I must have been asleep at the keyboard or maybe it's strawman language that slipped through into the spec. without adequate review...

# Oliver Hunt (12 years ago)

On Jul 12, 2013, at 8:58 AM, Luke Hoban <lukeh at microsoft.com> wrote:

Yeah, I think Math would have been less surprising. I don't feel strongly, but Number.prototype wasn't what I had expected.

I agree with luke, Math.clz seems like a better place than the prototype, otoh there's a nice conciseness to foo.clz()

Sorry, I meant isInteger. Per your quoted section, toInteger(Infinity) is Infinity, so isInteger(Infinity) is true.

I agree with MarkM that it seems bizarre that non-finite numbers may return true, but i think this is rooted in .isInteger() sounding like it means a 32bit integer (such that bitops won't modify the value) when it is actually a "no fractional component" test.

# Mark S. Miller (12 years ago)

No. Even if toInteger meant "no fractional component", I would still expect it only to return true if there is some specific mathematical integer that the JS number can be said to exactly represent. For the same reason, I think isInteger(-0) should be true and isInteger(NaN) should be false.

# Tab Atkins Jr. (12 years ago)

On Fri, Jul 12, 2013 at 10:19 AM, Mark S. Miller <erights at google.com> wrote:

No. Even if toInteger meant "no fractional component", I would still expect it only to return true if there is some specific mathematical integer that the JS number can be said to exactly represent. For the same reason, I think isInteger(-0) should be true and isInteger(NaN) should be false.

Agreed. isInteger() has one job, and it's kinda worthless if it can't even do that. How it should be:

Number.isInteger(-0) == true
Number.isInteger(NaN) == false
Number.isInteger(Infinity) == false

And, because of what we discussed in the recent thread...

Number.isInteger(Math.pow(2,53)-1) == true
Number.isInteger(Math.pow(2,53)) == false
# Oliver Hunt (12 years ago)

On Jul 12, 2013, at 10:19 AM, Mark S. Miller <erights at google.com> wrote:

No. Even if toInteger meant "no fractional component", I would still expect it only to return true if there is some specific mathematical integer that the JS number can be said to exactly represent. For the same reason, I think isInteger(-0) should be true and isInteger(NaN) should be false.

Oh i agree with that, i was making an observation that the behaviour (i think) will confuse people just

# Allen Wirfs-Brock (12 years ago)

On Jul 12, 2013, at 10:27 AM, Tab Atkins Jr. wrote:

On Fri, Jul 12, 2013 at 10:19 AM, Mark S. Miller <erights at google.com> wrote:

No. Even if toInteger meant "no fractional component", I would still expect it only to return true if there is some specific mathematical integer that the JS number can be said to exactly represent. For the same reason, I think isInteger(-0) should be true and isInteger(NaN) should be false.

Agreed. isInteger() has one job, and it's kinda worthless if it can't even do that. How it should be:

Number.isInteger(-0) == true
Number.isInteger(NaN) == false
Number.isInteger(Infinity) == false
```js

And, because of what we discussed in the recent thread...

```js
Number.isInteger(Math.pow(2,53)-1) == true
Number.isInteger(Math.pow(2,53)) == false

In other words you want to define Number.isInteger to return true only if it's argument is an integer number in the range -(2^53-1)..2^53-1

That's a useful test and a plausible definition of Number.isInteger but it will also probably be surprising to programmers who are familiar with the esoterics of IEEE floats.

If we went that direction what should Number.toInteger do for values outside that range?

# Tab Atkins Jr. (12 years ago)

On Fri, Jul 12, 2013 at 10:48 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

In other words you want to define Number.isInteger to return true only if it's argument is an integer number in the range -(2^53-1)..2^53-1

That's a useful test and a plausible definition of Number.isInteger but it will also probably be surprising to programmers who are familiar with the esoterics of IEEE floats.

On the other hand, people (like me) who just want to be able to tell when something is a freaking integer will be better served by these semantics. ^_^

If we went that direction what should Number.toInteger do for values outside that range?

The internal operation just returns 0 for things that aren't integers. I'd be okay with that, I suppose. (I use a similar function in some of my own code, and return false from the operation when the number cant' be converted, but that's because I use it as a combination tester and converter. We have an explicit tester, so I'm fine with the converter hiding details of its conversion.)

# Mark S. Miller (12 years ago)

On Fri, Jul 12, 2013 at 11:00 AM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

On the other hand, people (like me) who just want to be able to tell when something is a freaking integer will be better served by these semantics. ^_^

I fully agree. It will also help some programmers avoid the mistake I made -- of including 2^53 in their own home-rolled test.

If we went that direction what should Number.toInteger do for values outside that range?

For consistency, it should wrap mod 2^53 of course.

Just kidding. I have no coherent opinion yet of what it should do. What are the use cases we imagine toInteger may be useful for?

# Allen Wirfs-Brock (12 years ago)

On Jul 12, 2013, at 1:48 PM, Mark S. Miller wrote:

Just kidding. I have no coherent opinion yet of what it should do. What are the use cases we imagine toInteger may be useful for?

Well in the spec. the equivalent operation is used in various algorithms that access collections that are Uint32 length limited. For example, accessing elements of TypedArrays

For example, checking a a typed array index against the length of the array:

function get(ta, index) {
     let intIndex = Number.toInteger(index);   //why isn't this index.toInteger(); ??
     let len = ta.length;
     if (!Number.isFinite(len)) return undefined
     if (intIndex >= len) return undefined;
     if (intIndex < len) return undefined;
     return ta[intIndex];
}

Note that this is a situation where it would be most convenient if toInteger just passed through infinities in which case the explicit isFinite test could be eliminated.

Also, in the above, I'm assuming the NaN converts to 0.

# Jeff Walden (12 years ago)

On 07/12/2013 10:27 AM, Tab Atkins Jr. wrote:

And, because of what we discussed in the recent thread...

Number.isInteger(Math.pow(2,53)-1) == true
Number.isInteger(Math.pow(2,53)) == false

I need to comment in the other thread again and push back against what people have said there, but that thread's issues aside entirely, this is very very wrong. 2^53 is an integer. That there are multiple mathematical integer values that, when converted to IEEE-754 format, are equal to 2^53 is irrelevant. An integer value, that operations claim is not an integer, is very very wat.

# Allen Wirfs-Brock (12 years ago)

On Jul 12, 2013, at 3:54 PM, Jeff Walden wrote:

I need to comment in the other thread again and push back against what people have said there, but that thread's issues aside entirely, this is very very wrong. 2^53 is an integer. That there are multiple mathematical integer values that, when converted to IEEE-754 format, are equal to 2^53 is irrelevant. An integer value, that operations claim is not an integer, is very very wat.

are you suggesting that if we want such an function, it should be named something else, such as isExactInteger, isPreciseInteger, isUnambiguousInteger, etc?

# Tab Atkins Jr. (12 years ago)

On Fri, Jul 12, 2013 at 3:54 PM, Jeff Walden <jwalden+es at mit.edu> wrote:

I need to comment in the other thread again and push back against what people have said there, but that thread's issues aside entirely, this is very very wrong. 2^53 is an integer. That there are multiple mathematical integer values that, when converted to IEEE-754 format, are equal to 2^53 is irrelevant. An integer value, that operations claim is not an integer, is very very wat.

2^53 + 2 is also an integer, but it's clearly outside the range of exactly-representable integers, where each integer has one and only one representation. You can't tell if the number you got is actually 2^53+2 or not - it might have been input as 2^53+3, or 2^53+.1, or any number of other starting inputs.

If you agree with that reasoning that 2^53+2 should return false from isInteger(), then you must agree that 2^53 should also return false.

If you don't agree with that reasoning, then I suppose you'd argue that all numbers > 2^53 should return true, since they're all forced into being represented as integers?

If neither of these describe your position, could you explain your position in more detail?

# Jeff Walden (12 years ago)

On 07/12/2013 04:03 PM, Allen Wirfs-Brock wrote:

are you suggesting that if we want such an function, it should be named something else, such as isExactInteger, isPreciseInteger, isUnambiguousInteger, etc?

Possibly, but I don't think so. Whether a value is exact or precise is a function not of the value itself, but of how it was computed. Math.pow(2, 53) computed that way is an exact value. Math.pow(2, 53) - 1 + 2 is (in IEEE-754 terms) the same value. But it is not exact, because it derived from an inexact computation. It all depends how you got the value you're passing in.

isUnambiguousInteger is in a different league from exact/precise. Assuming a definition like so, it might be reasonable:

function isUnambiguousInteger(n)
{
  if ((n % 1) !== 0)
    return false;
  return Math.abs(n) < Math.pow(2, 53);
}

I'm not sure whether it would be useful enough to carry weight, tho, given it has fairly esoteric use cases. And anyone asking these sorts of questions really needs to know the IEEE-754 design well enough to understand how/why things go wrong, well enough that they could code it themselves. The existence of such a method doesn't make it any more likely that they will understand these details.

# Jeff Walden (12 years ago)

On 07/12/2013 04:13 PM, Tab Atkins Jr. wrote:

If you don't agree with that reasoning, then I suppose you'd argue that all numbers > 2^53 should return true, since they're all forced into being represented as integers?

All numbers >= 2^53 except Infinity, yes. I think "isInteger" implies the mathematical concept, with the only addition that it should pass -0. And while it would somewhat unfortunately diverge from the ToInteger spec operation, "toInteger" should imply the mathematical concept as well, and only produce values that are mathematical integers. ("toInteger" should probably convert -0 to -0 for consistency with "isInteger" on -0, but probably I could go either way here.)

# Allen Wirfs-Brock (12 years ago)

On Jul 12, 2013, at 4:18 PM, Jeff Walden wrote:

Possibly, but I don't think so. Whether a value is exact or precise is a function not of the value itself, but of how it was computed. Math.pow(2, 53) computed that way is an exact value. Math.pow(2, 53) - 1 + 2 is (in IEEE-754 terms) the same value. But it is not exact, because it derived from an inexact computation. It all depends how you got the value you're passing in.

So the other thread was a discussion concerning the appropriate value of Number.MAX_INTEGER. Do you think it should be 2^53-1, or 2^53, or the same thing as Math.MAX_VALUE.

# Jorge Chamorro (12 years ago)

On 13/07/2013, at 01:24, Jeff Walden wrote:

On 07/12/2013 04:13 PM, Tab Atkins Jr. wrote:

If you don't agree with that reasoning, then I suppose you'd argue that all numbers > 2^53 should return true, since they're all forced into being represented as integers?

All numbers >= 2^53 except Infinity, yes. I think "isInteger" implies the mathematical concept, with the only addition that it should pass -0. And while it would somewhat unfortunately diverge from the ToInteger spec operation, "toInteger" should imply the mathematical concept as well, and only produce values that are mathematical integers. ("toInteger" should probably convert -0 to -0 for consistency with "isInteger" on -0, but probably I could go either way here.)

Everything from Math.pow(2,52) to Math.pow(2,53) are integers (if represented as IEE-754 doubles), because there's no bit left to represent Math.pow(2,-1):

Math.pow(2,52)
4503599627370496

Math.pow(2,52).toString(2)
"10000000000000000000000000000000000000000000000000000"

Math.pow(2,52).toString(2).length
53

(Math.pow(2,52)-1).toString(2)
"1111111111111111111111111111111111111111111111111111"

(Math.pow(2,52)-1).toString(2).length
52

Math.pow(2,52)-0.5
4503599627370495.5

Math.pow(2,52)+0.5
4503599627370496
# Jeff Walden (12 years ago)

On 07/12/2013 04:32 PM, Allen Wirfs-Brock wrote:

So the other thread was a discussion concerning the appropriate value of Number.MAX_INTEGER. Do you think it should be 2^53-1, or 2^53, or the same thing as Math.MAX_VALUE.

Number.MAX_INTEGER should be 2^53. People who want 2^53 - 1 (and there are roughly reasonable uses for it as discussed in that thread, if enough care is taken) can use < as the relevant operator when comparing. In contrast, if the value were 2^53 - 1, people who want the 2^53 value can't simply use a different operator.

I haven't kept up enough with this list to know what Math.MAX_VALUE is, and it's not in the latest draft I have -- unless you meant Number.MAX_VALUE? If you meant that, Number.MAX_INTEGER should definitely be different from Number.MAX_VALUE.

# Allen Wirfs-Brock (12 years ago)

On Jul 12, 2013, at 4:39 PM, Jeff Walden wrote:

Number.MAX_INTEGER should be 2^53. People who want 2^53 - 1 (and there are roughly reasonable uses for it as discussed in that thread, if enough care is taken) can use < as the relevant operator when comparing. In contrast, if the value were 2^53 - 1, people who want the 2^53 value can't simply use a different operator.

Number.MAX_INTEGER+1 ??

I haven't kept up enough with this list to know what Math.MAX_VALUE is, and it's not in the latest draft I have -- unless you meant Number.MAX_VALUE? If you meant that, Number.MAX_INTEGER should definitely be different from Number.MAX_VALUE.

Sorry, I meant Number.MAX_VALUE. So you seem to be saying that that Number.isInteger(MAX_VALUE) should be true, but that Number.MAX_VALUE > Number.MAX_INTEGER is also true because for isInteger you using the mathematical definition of "Integer" but for MAX_INTEGER you are using some other definition of "INTEGER".

# Jeff Walden (12 years ago)

On 07/12/2013 04:56 PM, Allen Wirfs-Brock wrote:

So you seem to be saying that that Number.isInteger(MAX_VALUE) should be true, but that Number.MAX_VALUE > Number.MAX_INTEGER is also true because for isInteger you using the mathematical definition of "Integer" but for MAX_INTEGER you are using some other definition of "INTEGER".

I think so. Although, I am not at all wedded to the MAX_INTEGER name, it's just what was proposed already. :-) Your implied point is well-taken that "max integer" is a misnomer. MAX_EXACT_INTEGER, perhaps? Maybe? I dunno. There surely must be some API with prior art for a name here, but I can't immediately find it in web searches right now.

# Domenic Denicola (12 years ago)

While I sympathize with the desire to make "integer" mean "mathematical integer," I don't think it's going to work out very well. Nobody actually cares about such functions, and you of course have the WATs of

Number.isInteger(9007199254740992.5) === true

since the runtime couldn't distinguish this from 9007199254740992.

In practice all this ends up doing is forcing our currently-proposed useful functions/constants to have an "exact"/"EXACT" inserted into them.

I think it would be easier just to accept that "integer" in JS does not mean "mathematical integer" but instead means "unambiguously representable integer," since that is operationally the useful definition.

# Mark S. Miller (12 years ago)

isSafeInteger, toSafeInteger, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER

Like "exact", "unambiguous", etc, it is not immediately obvious what "safe" means. Unlike them, it is obvious that it is not obvious what it means -- that you don't know exactly what it means until you hear an explanation.

# Tab Atkins Jr. (12 years ago)

On Fri, Jul 12, 2013 at 5:15 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

While I sympathize with the desire to make "integer" mean "mathematical integer," I don't think it's going to work out very well. Nobody actually cares about such functions, and you of course have the WATs of

Number.isInteger(9007199254740992.5) === true

since the runtime couldn't distinguish this from 9007199254740992.

This is what I was trying to point out as a ridiculous possibility in Jeff's idea, except he claimed it's what he actually wanted. ;_;

In practice all this ends up doing is forcing our currently-proposed useful functions/constants to have an "exact"/"EXACT" inserted into them.

I think it would be easier just to accept that "integer" in JS does not mean "mathematical integer" but instead means "unambiguously representable integer," since that is operationally the useful definition.

Yes.

# Jeff Walden (12 years ago)

On 07/12/2013 06:17 PM, Tab Atkins Jr. wrote:

On Fri, Jul 12, 2013 at 5:15 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

While I sympathize with the desire to make "integer" mean "mathematical integer," I don't think it's going to work out very well. Nobody actually cares about such functions, and you of course have the WATs of

Number.isInteger(9007199254740992.5) === true

since the runtime couldn't distinguish this from 9007199254740992.

This is what I was trying to point out as a ridiculous possibility in Jeff's idea, except he claimed it's what he actually wanted. ;_;

Roughly no one will type something like that. :-) And if the "value" were the result of an operation that lost precision, there's no way to tell that with an API that tells you if the value was an integer. Indeed, asking if the value is an integer seems a bit of a non sequitur to me, for that concern.

# Tab Atkins Jr. (12 years ago)

On Fri, Jul 12, 2013 at 6:39 PM, Jeff Walden <jwalden+es at mit.edu> wrote:

Roughly no one will type something like that. :-) And if the "value" were the result of an operation that lost precision, there's no way to tell that with an API that tells you if the value was an integer.

Exactly, which is why we can only accurately answer for numbers <= 2^53-1. Anything larger might have lost precision.

Technically, smaller things may lose precision as well - 2^52 + .5 == 2^52. But expecting precision out of decimals is a mugs game anyway. As long as you stick to integers, you can be sure of your precision for <= 2^53-1, but as soon as you hit 2^53, you're no longer sure.

# Domenic Denicola (12 years ago)

From: Tab Atkins Jr. [mailto:jackalmage at gmail.com]

Exactly, which is why we can only accurately answer for numbers <= 2^53-1.

Probably a horrible idea in practice, but I feel like the correct answer here is throwing outside that range. It's like asking "is Tab's second head blonde or brunette?" The only appropriate answer seems to be "does not compute."

(I think this might be my worst analogy yet. Woohoo!)