TL;DR
It depends largely on what you want to parse as a number.
Comparison Between Built-in Functions
As none of the existing sources satisfied my soul, I tried to figure out what actually was happening with these functions.
Three immediate answers to this question felt like:
!isNaN[input]
[which gives the same output as+input === +input
]!isNaN[parseFloat[input]]
isFinite[input]
But are any of them correct in every scenario?
I tested these functions in several cases, and generated output as markdown. This is what it looks like:
123 | ✔️ | ✔️ | ✔️ | - |
'123' | ✔️ | ✔️ | ✔️ | - |
12.3 | ✔️ | ✔️ | ✔️ | - |
'12.3' | ✔️ | ✔️ | ✔️ | - |
' 12.3 ' | ✔️ | ✔️ | ✔️ | Empty whitespace trimmed, as expected. |
1_000_000 | ✔️ | ✔️ | ✔️ | Numeric separator understood, also expected. |
'1_000_000' | ❌ | ✔️ | ❌ | Surprise! JS just won't parse numeric separator inside a string. For details, check this issue. [Why then parsing as float worked though? Well, it didn't. 😉] |
'0b11111111' | ✔️ | ✔️ | ✔️ | Binary form understood, as it should've. |
'0o377' | ✔️ | ✔️ | ✔️ | Octal form understood too. |
'0xFF' | ✔️ | ✔️ | ✔️ | Of course hex is understood. Did anybody think otherwise? 😒 |
'' | ✔️ | ❌ | ✔️ | Should empty string be a number? |
' ' | ✔️ | ❌ | ✔️ | Should a whitespace-only string be a number? |
'abc' | ❌ | ❌ | ❌ | Everybody agrees, not a number. |
'12.34Ab!@#$' | ❌ | ✔️ | ❌ | Ah! Now it's quite understandable what parseFloat[] does. Not impressive to me, but may come handy in certain cases.
|
'10e100' | ✔️ | ✔️ | ✔️ | 10100 is a number indeed. But caution! It's way more larger than the maximum safe integer value 253 [about 9×1015]. Read this for details. |
'10e1000' | ✔️ | ✔️ | ❌ | Say with me, help! Though not as crazy as it may seem. In JavaScript, a value larger than ~10308 is rounded to infinity, that's why. Look here for details. And yes, isNaN[] considers infinity as a number, and parseFloat[] parses infinity as infinity.
|
null | ✔️ | ❌ | ✔️ | Now this is awkward. In JS, when a conversion is needed, null becomes zero, and we get a finite number. Then why parseFloat[null] should return a NaN here? Someone please explain this design concept to me.
|
undefined | ❌ | ❌ | ❌ | As expected. |
Infinity | ✔️ | ✔️ | ❌ | As explained before, isNaN[] considers infinity as a number, and parseFloat[] parses infinity as infinity.
|
So...which of them is "correct"?
Should be clear by now, it depends largely on what we need. For example, we may want to consider a null input as 0. In that case isFinite[]
will work fine.
Again, perhaps we will take a little help from isNaN[]
when 1010000000000 is needed to be considered a valid number [although the question remains—why would it be, and how would we handle that]!
Of course, we can manually exclude any of the scenarios.
Like in my case, I needed exactly the outputs of isFinite[]
, except for the null case, the empty string case, and the whitespace-only string case. Also I had no headache about really huge numbers. So my code looked like this:
/**
* My necessity was met by the following code.
*/
if [input === null] {
// Null input
} else if [input.trim[] === ''] {
// Empty or whitespace-only string
} else if [isFinite[input]] {
// Input is a number
} else {
// Not a number
}
And also, this was my JavaScript to generate the table:
/**
* Note: JavaScript does not print numeric separator inside a number.
* In that single case, the markdown output was manually corrected.
* Also, the comments were manually added later, of course.
*/
let inputs = [
123, '123', 12.3, '12.3', ' 12.3 ',
1_000_000, '1_000_000',
'0b11111111', '0o377', '0xFF',
'', ' ',
'abc', '12.34Ab!@#$',
'10e100', '10e1000',
null, undefined, Infinity];
let markdownOutput = `| \`input\` | \`!isNaN[input]\` or
\`+input === +input\` | \`!isNaN[parseFloat[input]]\` | \`isFinite[input]\` | Comment |
| :---: | :---: | :---: | :---: | :--- |\n`;
for [let input of inputs] {
let outputs = [];
outputs.push[!isNaN[input]];
outputs.push[!isNaN[parseFloat[input]]];
outputs.push[isFinite[input]];
if [typeof input === 'string'] {
// Output with quotations
console.log[`'${input}'`];
markdownOutput += `| '${input}'`;
} else {
// Output without quotes
console.log[input];
markdownOutput += `| ${input}`;
}
for [let output of outputs] {
console.log['\t' + output];
if [output === true] {
markdownOutput += ` | true`;
// markdownOutput += ` | ✔️`; // for stackoverflow
} else {
markdownOutput += ` | false`;
// markdownOutput += ` | ❌`; // for stackoverflow
}
}
markdownOutput += ` ||\n`;
}
// Replace two or more whitespaces with $nbsp;
markdownOutput = markdownOutput.replaceAll[` `, ` `];
// Print markdown to console
console.log[markdownOutput];