parseInt vs unary plus, when to use which?
Javascriptnode.jsJavascript Problem Overview
What are the differences between this line:
var a = parseInt("1", 10); // a === 1
and this line
var a = +"1"; // a === 1
This jsperf test shows that the unary operator is much faster in the current chrome version, assuming it is for node.js!?
If I try to convert strings which are not numbers both return NaN
:
var b = parseInt("test" 10); // b === NaN
var b = +"test"; // b === NaN
So when should I prefer using parseInt
over the unary plus (especially in node.js)???
edit: and what's the difference to the double tilde operator ~~
?
Javascript Solutions
Solution 1 - Javascript
The ultimate whatever-to-number conversion table:
EXPRS = [
'parseInt(x)',
'parseFloat(x)',
'Number(x)',
'+x',
'~~x',
'x>>>0',
'isNaN(x)'
];
VALUES = [
'"123"',
'"+123"',
'"-123"',
'"123.45"',
'"-123.45"',
'"12e5"',
'"12e-5"',
'"0123"',
'"0000123"',
'"0b111"',
'"0o10"',
'"0xBABE"',
'"4294967295"',
'"123456789012345678"',
'"12e999"',
'""',
'"123foo"',
'"123.45foo"',
'" 123 "',
'"foo"',
'"12e"',
'"0b567"',
'"0o999"',
'"0xFUZZ"',
'"+0"',
'"-0"',
'"Infinity"',
'"+Infinity"',
'"-Infinity"',
'BigInt(1)',
'null',
'undefined',
'true',
'false',
'Infinity',
'NaN',
'{}',
'{valueOf: function(){return 42}}',
'{toString: function(){return "56"}}',
];
//////
function wrap(tag, s) {
if (s && s.join)
s = s.join('');
return '<' + tag + '>' + String(s) + '</' + tag + '>';
}
function table(head, rows) {
return wrap('table', [
wrap('thead', tr(head)),
wrap('tbody', rows.map(tr))
]);
}
function tr(row) {
return wrap('tr', row.map(function (s) {
return wrap('td', s)
}));
}
function val(n) {
return n === true || Number.isNaN(n) || n === "Error" ? wrap('b', n) : String(n);
}
var rows = VALUES.map(function (v) {
var x = eval('(' + v + ')');
return [v].concat(EXPRS.map(function (e) {
try {
return val(eval(e));
} catch {
return val("Error");
}
}));
});
document.body.innerHTML = table(["x"].concat(EXPRS), rows);
table { border-collapse: collapse }
tr:nth-child(odd) { background: #fafafa }
td { border: 1px solid #e0e0e0; padding: 5px; font: 12px monospace }
td:not(:first-child) { text-align: right }
thead td { background: #3663AE; color: white }
b { color: red }
Solution 2 - Javascript
Well, here are a few differences I know of:
-
An empty string
""
evaluates to a0
, whileparseInt
evaluates it toNaN
. IMO, a blank string should be aNaN
.+'' === 0; //true isNaN(parseInt('',10)); //true
-
The unary
+
acts more likeparseFloat
since it also accepts decimals.parseInt
on the other hand stops parsing when it sees a non-numerical character, like the period that is intended to be a decimal point.
.+'2.3' === 2.3; //true parseInt('2.3',10) === 2; //true
-
parseInt
andparseFloat
parses and builds the string left to right. If they see an invalid character, it returns what has been parsed (if any) as a number, andNaN
if none was parsed as a number.The unary
+
on the other hand will returnNaN
if the entire string is non-convertible to a number.parseInt('2a',10) === 2; //true parseFloat('2a') === 2; //true isNaN(+'2a'); //true
-
As seen in the comment of @Alex K.,
parseInt
andparseFloat
will parse by character. This means hex and exponent notations will fail since thex
ande
are treated as non-numerical components (at least on base10).The unary
+
will convert them properly though.parseInt('2e3',10) === 2; //true. This is supposed to be 2000 +'2e3' === 2000; //true. This one's correct. parseInt("0xf", 10) === 0; //true. This is supposed to be 15 +'0xf' === 15; //true. This one's correct.
Solution 3 - Javascript
The table in thg435's answer I believe is comprehensive, however we can summarize with the following patterns:
- Unary plus does not treat all falsy values the same, but they all come out falsy.
- Unary plus sends
true
to 1, but"true"
toNaN
. - On the other hand,
parseInt
is more liberal for strings that are not pure digits.parseInt('123abc') === 123
, whereas+
reportsNaN
. Number
will accept valid decimal numbers, whereasparseInt
merely drops everything past the decimal. ThusparseInt
mimics C behavior, but is perhaps not ideal for evaluating user input.- Both trim whitespace in strings.
parseInt
, being a badly designed parser, accepts octal and hexadecimal input. Unary plus only takes hexademical.
Falsy values convert to Number
following what would make sense in C: null
and false
are both zero. ""
going to 0 doesn't quite follow this convention but makes enough sense to me.
Therefore I think if you are validating user input, unary plus has correct behavior for everything except it accepts decimals (but in my real life cases I'm more interested in catching email input instead of userId, value omitted entirely, etc.), whereas parseInt is too liberal.
Solution 4 - Javascript
Be carefull, parseInt is faster than + unary operator in Node.JS, it's false that + or |0 are faster, them are faster only for NaN elements.
Check this out:
var arg=process.argv[2];
rpt=20000;
mrc=1000;
a=[];
b=1024*1024*1024*1024;
for (var i=0;i<rpt;i++)
a[i]=Math.floor(Math.random()*b)+' ';
t0=Date.now();
if ((arg==1)||(arg===undefined))
for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
c=a[i]-0;
}
t1=Date.now();
if ((arg==2)||(arg===undefined)) {
for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
d=a[i]|0;
}
}
t2=Date.now();
if ((arg==3)||(arg===undefined)) {
for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
e=parseInt(a[i]);
}
}
t3=Date.now();
if ((arg==3)||(arg===undefined)) {
for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
f=+a[i];
}
}
t4=Date.now();
console.log(a[i-1],c,d,e,f);
console.log('Eseguiti: '+rpt*mrc+' cicli');
console.log('parseInt '+(t3-t2));
console.log('|0 '+(t2-t1));
console.log('-0 '+(t1-t0));
console.log('+ '+(t4-t3));
Solution 5 - Javascript
I recommend use Math.floor (or ~~ if you know numbers are positive) instead of parseString. +(expression) is out of scope, because +(expression) is more like parseFloat. Look this little benchmark:
// 1000000 iterations each one
node test_speed
Testing ~~, time: 5 ms
Testing parseInt with number, time: 25 ms
Testing parseInt with string, time: 386 ms
Testing Math.floor, time: 18 ms
The source code of benchmark:
/* el propósito de este script es evaluar
que expresiones se ejecutan más rápido para así
decidir cuál usar */
main()
async function main(){
let time, x
let number = 23456.23457
let test1 = ()=>{
x = 0
time = Date.now()
for(let i=0;i<1000000;i++){
let op = Math.floor(number / 3600)
x = op
}
console.info("Testing Math.floor, time:", Date.now() - time, "ms")
}
let test2 = ()=>{
x = 0
time = Date.now()
for(let i=0;i<1000000;i++){
let op = parseInt(number / 3600)
x = op
}
console.info("Testing parseInt with number, time:", Date.now() - time, "ms")
}
let test3 = ()=>{
x = 0
time = Date.now()
for(let i=0;i<1000000;i++){
let op = parseInt((number / 3600).toString())
x = op
}
console.info("Testing parseInt with string, time:", Date.now() - time, "ms")
}
let test4 = ()=>{
x = 0
time = Date.now()
for(let i=0;i<1000000;i++){
let op = ~~(number / 3600)
x = op
}
console.info("Testing ~~, time:", Date.now() - time, "ms")
}
test4()
test2()
test3()
test1()
}
Solution 6 - Javascript
Consider performance too. I was suprised that parseInt
beats unary plus on iOS :) This is helpful for web apps with heavy CPU consumption only. As a rule-of-thumb I'd suggest JS opt-guys to consider any JS operator over another one from the mobile performance point of view nowadays.
So, go mobile-first ;)