-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
_.isNumber(NaN) returns true #406
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 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 |
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.
Possibly, sure.
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 |
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: |
Not to beat a dead horse but... Why not just do 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 |
+1 on making the library more useful and in the same instance educate users new to JavaScript. Suggestion: 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 Alternatively, since _.isFinite = function (value) {
return value > -1 / 0 && value < 1 / 0;
}; |
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? |
|
I agree. |
I did not say rename... |
I was surprised too to find out that I think I tend to agree with @contentfree’s #406 (comment). 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¢…™ |
in normal sense |
I vote for semantic constructiveness, |
Naw, it's a number by |
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. |
I recently aligned an implementation of No need to change the behavior of
See my comment about validation, |
@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. |
In that context is it any more counter intuitive than These are I've already proposed a viable alternative which is to simply make Look to underscore-contrib for more fine grained number methods. It has |
@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 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:
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. |
Yeah but its called "Not a Number". |
@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 |
|
@pspi Agreed. Making And of course, one day after posting this, I discover that |
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.
The text was updated successfully, but these errors were encountered: