Underscore: _.isNumber(NaN) returns true

Created on 15 Dec 2011  ·  38Comments  ·  Source: jashkenas/underscore

As NaN stands for "Not a Number" the isNumber check in this case seems like it should return false. I notice from other discussions that this is in fact on purpose. Maybe the documentation should reflect this fact explicitly.

enhancement fixed

Most helpful comment

Ok so I started with a simple problem. I have inputs that are strings, numbers and possibly NaN. I have a simple solution in base JavaScript utilizing parseInt and isFinite to test if the parse int succeeds. Simple but not fully clean or self descriptive of my goal. So, I decide to use my totally awesome goto library for doing these sort of tasks. I find a function on initial inspection that says it takes a value and tells you if it is a number, which by my common definition is what I think I want.

First, by this can you see how adding a line to the documentation would help users of the library get the right function for the job the first time out? Second, I ask again for an example where this is meaningful.

It sounds like you know everything there is to know about the language. So, rather than spouting back blind shot in the dark solutions that my 8th grade brother could give me, maybe it would be nice if you could use some of that mastery schooling the rest of us via awesome documentation. Thank you for your time.

All 38 comments

You're right, it is on purpose. And I don't think it needs to be explicitly stated. Anyone that's ever worked with IEEE 754 floats before probably knows that NaN is just another floating point value.

Related: discussion in #321

My point in asking is that, when I am doing common web tasks, a function named isNumber seems like a good way of checking if a value that is found answers the common user definition of "is this a number?".

In addition, it seems to me that the NaN value was introduced in the first place to help identify values that that break computations. In the spirit of the spec it seems like the correct answer when you ask the question "is this a number?" is in fact that it is "Not a Number". If you can name a common example where you would want the answer to be true for this question I'll shut up now :)

You're looking for isFinite, which people that know JS should already know.

Ok so I started with a simple problem. I have inputs that are strings, numbers and possibly NaN. I have a simple solution in base JavaScript utilizing parseInt and isFinite to test if the parse int succeeds. Simple but not fully clean or self descriptive of my goal. So, I decide to use my totally awesome goto library for doing these sort of tasks. I find a function on initial inspection that says it takes a value and tells you if it is a number, which by my common definition is what I think I want.

First, by this can you see how adding a line to the documentation would help users of the library get the right function for the job the first time out? Second, I ask again for an example where this is meaningful.

It sounds like you know everything there is to know about the language. So, rather than spouting back blind shot in the dark solutions that my 8th grade brother could give me, maybe it would be nice if you could use some of that mastery schooling the rest of us via awesome documentation. Thank you for your time.

I'm interested in what @jashkenas thinks. I don't mean to sound insulting or condescending. I know I come off like that sometimes. I think it's the fact that I'm trying to steer the conversation in a direction that better shows off the problem by giving these simple answers and seeing how they fail to solve the problem in your eyes.

First, by this can you see how adding a line to the documentation would help users of the library get the right function for the job the first time out?

Possibly, sure.

Second, I ask again for an example where this is meaningful.

Testing for [[Class]] == "Number"? That should be obvious, it matches numbers. Maybe they're not natural numbers or integers or finite numbers or real numbers or rational numbers or whatever subset you were expecting, but they are surely all numbers.

All that said, there are times when adding too much fluff in the documentation can be harmful. But I don't think this is one of those times. This could potentially be helpful for developers that may, in their current state, have a conflicting interpretation of the abstract concept of a number. Like in your case, where you already had established a mental model of the set for which you wanted to test, and then found a function that _appeared_ to perform that test. +1, though I'm still interested in what @jashkenas thinks.

Yes -- it's not at all obvious whether _.isNumber will return true or false when passed NaN. I certainly wouldn't remember without trying it. Added a note to the documentation in the above commit. Thanks.

Thanks guys. Glad to know that someone else might not run into this same issue.

This definitely seems like a case where semantics should trump technical facts. Yes, NaN may be a Number according to some spec. But if I ask something the question "Are you a number?" and it says "I am Not a Number" then I should believe it. Not a Number – NaN – definitively is not a Number... and isNumber should return false.

Putting the Gotcha in the docs – instead of fixing things to make sense to humans – just leads to less time coding and more time peering over documentation scratching your head.

Can this please be reconsidered?

@contentfree: NaN is a member of the floats. Every number in JS is a float. I think that makes NaN as number-ey as they get. NaN does not mean "I am not a number". It's the numeric representation of non-numbers. Case closed in my book. Use isFinite if you want to test for numbers that aren't NaN/Infinity/-Infinity.

Not to beat a dead horse but...

Why not just do
obj instanceOf Number

Seems to me that if there is a more readable way of doing the necessary action that already exists in the javascript specification I'm going to do it that way not using a library with an unknown(unless I waste time investigating) library.

I vote that if we are not going to fix this to be semantically correct we should just remove the useless overhead and headache from the library.

The instanceof operator will only work for objects, not primitives: new Number(5) instanceof Number == true; 5 instanceof Number == false. It also won't work across frames in browsers. [[Class]] checking via Object::toString is generally accepted to be the most reliable type checking method in JavaScript.

+1 on making the library more useful and in the same instance educate users new to JavaScript.

Suggestion:
_.isNumber(object, [isFinite]);

This will allow for a simple addition of a true to your current implementation when caught by this gotcha while learning that isFinite was probably what you were after in the first place.

My 2c

@nickl- Interesting idea, but...why not simply use isFinite in that case?

Alternatively, since _.isNaN already exists, _.isFinite could potentially be added for symmetry:

_.isFinite = function (value) {
  return value > -1 / 0 && value < 1 / 0;
};

@kitcambridge

The only issue I foresee with that is that strings would pass as finite "0x0", "0xF", "2", etc. So no matter what, it will need to be combined with _.isNumber or equivalent:

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && _.isNumber(obj);
};

Can shave off five chars by using val === +val for number testing:

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && obj === +obj;
};

@octatone Sure, I think that's fair...technically, those strings _are_ finite, as they can be used in numeric comparisons (the interpreter should coerce them to numbers), but your proposal is more consistent with the existing Underscore type checking functions.

What about _,isValidNumber which is inline with the existing _.isValidDate and does not confuse the isFinite argument?

@nickl-

NaN and Infinity _are_ valid numbers. I think that naming this function isValidNumber would confuse its purpose or do you mean rename isNumber to isValidNumber?

NaN and Infinity _are_ valid numbers. I think that naming this function isValidNumber would confuse its purpose.

I agree.

I did not say rename...

I was surprised too to find out that _.isNumber(NaN) === true.

I think I tend to agree with @contentfree’s https://github.com/jashkenas/underscore/issues/406#issuecomment-4144992. In software engineering we even have a principle for this specific case: Principle Of Least Astonishment. ;-)

And after seeing this many people confused about it, I think it would make sense to get the function to do what non-hard-core-js-coders humans expect it to do when they look at its name.

Just my little 2¢…™

_.isNaN is also confusing. In the document :

Note: this is not the same as the native isNaN function, which will also return true for many other not-number values, such as undefined.

in normal sense undefined is indeed Not-A-Number.

I vote for semantic constructiveness, isNumber == Not a number lol. I believe, If you need to go into detail about checking for floating point values, then do so some other way?

I vote for semantic constructiveness,

Naw, it's a number by [[Class]] just like -Infinity, Infinity, & Object(2). It's one of those things devs learn, like functions are objects too. Chances are you'll want to do some form of validation on your number which is out of scope for this method. For example is it greater than -1, less than Math.pow(2,32), or a whole number. In cases of -Infinity, Infinity or NaN I use _.isFinite as the validator. Similarly _.isDate doesn't validate if the date object represents a valid date.

What about updating the documentation with a "see also: isFinite" and "see also: «essay enumerating methods of determining if something is a numeric value that you can actually use»"

@michaelficarra I know that in the spec NaN is very much a numeric type. But is that what a programmer is thinking when they ask is this a number?

What most of us want to know, most of the time is, can I use this for basic arithmetic. So you have to go isNumber(x) && isFinite(x). And that is okay I guess. But it's a big gotcha for a new programmer and does not read well.

From a strictly English language point of view Not a Number (NaN) returning true from a test called isNumber makes no sense at all. Wouldn't this be better named isNumeric or isNumericType?

I have no doubt that this will add bugs and waste many hours, at least the first time people come up against this.

So you have to go isNumber(x) && isFinite(x).

I recently aligned an implementation of _.isFinite to follow ES6 Number.isFinite.
It ensures the value is a number primitive, is finite, & should handle this common case.

No need to change the behavior of _.isNumber which aligns with the other [[Class]] check methods.

From a strictly English language point of view Not a Number (NaN) returning true from a test called isNumber makes no sense at all. Wouldn't this be better named isNumeric or isNumericType?

See my comment about validation, _.isNumber, and _.isDate.

@jdalton You are correct about the behaviour being constant with _.isDate. And you guys do great work with underscore, so I think the decision really falls to you.

But it just seems so counter intuitive that something called not a number is a number.

@Walms 100% agree. I think in this case, the programmer and intuition should overrule the absolute "correctness" of the statement given NaN is _technically_ numeric.

But it just seems so counter intuitive that something called not a number is a number.

In that context is it any more counter intuitive than _.isNumber returning true for number objects, Object(2) that are type-of object?

These are [[Class]] methods. Maybe that needs to be made more clear in the documentation.

I've already proposed a viable alternative which is to simply make _.isFinite follow ES6 Number.isFinite. If you want a basic is-number use typeof x == 'number'. If you want some validation that the value isn't NaN, Infinity, or -Infinity, all [[Class]] of Number, then _.isFinite is the way to go.

Look to underscore-contrib for more fine grained number methods. It has _.isNumeric, _.isInteger, _.isZero, _.isEven, _.isOdd, _.isFloat, _.isNegative, & _.isPositive.

@jdalton I think you last two comments have really helped clarify why I think this is confusing.

I did not initially see that _.isNumber was given a context by the fact that it was prefixed by is, meaning that it was a type check. With this context you are totally correct it makes no sense for _.isNumber to return false for NaN.

But then underscore-contrib and isFinite seem to break this context. As they start with is, yet are looking at value rather than type. And this is where I think the confusion is there is a no clear way of determining the context from the function name.

All this said I don't see any way of fixing this.

I think the issue here is that there are some things about Numbers that are non-intuitive. However, they do behave in a consistent way. It would be possible to make _.isNumber more intuitive in this case by making it less consistent, which ultimately would make it less intuitive in other cases.

Here are some concrete examples:

# If you add two Numbers together you always get another Number back
# This function should always return true no matter what you pass it
closedUnderAddition = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a + b)

closedUnderAddition(1,1) == true
closedUnderAddition(Number.MAX_INT,2**970) == true # false if isNumber checks finiteness

# If you divide two Numbers you always get another Number back
closedUnderDivision = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a / b)

closedUnderDivision(1, 2) == true
closedUnderDivision(1, 0) == true # false if isNumber checks finiteness
closedUnderDivision(0, 0) == true # false if isNumber checks finiteness or NaN-ness

# Anything you cast to a Number is a Number
castsToNumber = (x) -> isNumber(Number(x))

castsToNumber(1) == true
castsToNumber(Infinity) == true # false if isNumber checks finiteness
castsToNumber("bees") == true # false if isNumber checks finiteness or NaN-ness
castsToNumber("3") == true

As michaelficarra said:

NaN does not mean "I am not a number". It's the numeric representation of non-numbers.

Or, to put it another way, there are "numbers" and Numbers. NaN might not be a "number" but it is absolutely a Number along with Infinity, -Infinity and ridiculous things like -0. As wild and wooly as it is, though, the definition of Number is well-specified and behaves consistently.

"number" on the other hand is a badly-defined concept that, depending on who you're talking to, could include any or none of the above, and maybe numeric strings, bools, midnight and all sorts of things. That's fine, but there's never going to be a solution that adheres to everyone's definition.

I think for that reason this is essentially a documentation issue.

Also has "number" stopped looking like a word for anyone else?

Good points, @sgentle. I vote for consistency over intuitiveness, since the former is an objective measure whereas we won't all agree on what's intuitive (as this thread demonstrates).

Just do not treat NaN as "Not a Number". NaN is a special value.

Just do not treat NaN as "Not a Number". NaN is a special value.

Yeah but its called "Not a Number".
To me that's like this
var _false = "true";
Yeah you can learn that _false is true, but it's confusing for no good reason.

@Walms It's a bad name but that's not JS's fault. We may blame the guy who named it as "NaN". http://en.wikipedia.org/wiki/NaN

Aargh, I wish _.isNumber(NaN) would return false... some serious head scratching over here until I realised this was the reason.

if (isNaN(Number(value))) {
  alert('Number required.');
}

@pspi Agreed. Making _.isNumber() behave correctly in the _semantic_ sense means I'll never actually use it. ._isFinite() appears to be the function that works the way I'd expect.

And of course, one day after posting this, I discover that _.isFinite('1') is true, even though the argument isn't a number.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jdalton picture jdalton  ·  6Comments

haggholm picture haggholm  ·  8Comments

jdalton picture jdalton  ·  4Comments

afranioce picture afranioce  ·  8Comments

arypbatista picture arypbatista  ·  3Comments