Why is it recommended to provide optional radix parameter to parseInt()? - javascript

I've always used the parseInt() function in Javascript without passing the radix parameter. As per the MDN documentation here, it's stated that not providing this parameter may result in unpredictable behaviour.
Always specify this parameter to eliminate reader confusion and to
guarantee predictable behavior.
Could somebody clarify what does this unpredictable behavior mean with some code examples?

In older versions of the language, parseInt() would cause the function to obey the normal JavaScript numeric constant syntax rules, including the recognition of a leading zero to denote octal constants, and a leading 0x to denote hex constants. Thus, if your code didn't explicitly insist on base 10, stray (possibly user-supplied) numbers with leading zeros would be interpreted as base-8 values, and leading 0x as hex.
The base-8 behavior is gone since ES5.1 (I think; might have been earlier), but the base 16 behavior is still there. (Probably a leading 0x is a little more rare as an accidental prefix than a simple leading 0.)
My experience looking at code here on Stack Overflow is that parseInt() is overused anyway. It's usually cleaner to convert strings (often, strings taken from DOM element .value properties) to numbers with the unary + operator:
var count = +document.getElementById("count").value;
That won't necessarily give you an integer, of course. However, what it will do is notice that the input string has trailing non-numeric garbage. The parseInt() function will simply stop parsing a string like "123abc" and give you 123 as the numeric value. The leading + will however give you a NaN.
If you need integers, you can always use Math.floor() or Math.round().
edit — a comment notes that ES2015 requires a leading 0o or 0O in "strict" mode for octal literals, but that doesn't apply to parseInt() which (in ES2015) only overrides the default radix for hex strings.

For some reason best known to themselves, the folk specifying the behaviour of this function set the radix to be a defaultable parameter but then decided to leave the default value up to the implementation! (Perhaps it would have been sensible to insist on a value of 10 but maybe that would have upset folk progamming in the 1970s who still consider octal literals to be useful.)
So for robust progamming, you need to supply the radix parameter yourself.

Without supplying the radix, parseInt tries to determine what the right radix is by the value you pass in, for example if the value starts 0x then it determines you must be passing in hex values. Same applies with 0 (octal).
This becomes problematic when your input is zero-padded but no radix is supplied. Where the result will (possibly) not be as expected
console.log(parseInt(015))

Related

Keep trailing or leading zeroes on number

Is it possible to keep trailing or leading zeroes on a number in javascript, without using e.g. a string instead?
const leading = 003; // literal, leading
const trailing = 0.10; // literal, trailing
const parsed = parseFloat('0.100'); // parsed or somehow converted
console.log(leading, trailing, parsed); // desired: 003 0.10 0.100
This question has been regularly asked (and still is), yet I don't have a place I'd feel comfortable linking to (did i miss it?).
Fully analogously would be keeping any other aspect of the representation a number literal was entered as, although asked nowhere near as often:
console.log(0x10); // 16 instead of potentially desired 0x10
console.log(1e1); // 10 instead of potentially desired 1e1
For disambiguation, this is not about the following topics, for some of which I'll add links, as they might be of interest as well:
Padding to a set amount of digits, formatting to some specific string representation, e.g. How can i pad a value with leading zeroes?, How to output numbers with leading zeros in JavaScript?, How to add a trailing zero to a price
Why a certain string representation will be produced for some number by default, e.g. How does JavaScript determine the number of digits to produce when formatting floating-point values?
Floating point precision/accuracy problems, e.g. console.log(0.1 + 0.2) producing 0.30000000000000004, see Is floating point math broken?, and How to deal with floating point number precision in JavaScript?
No. A number stores no information about the representation it was entered as, or parsed from. It only relates to its mathematical value. Perhaps reconsider using a string after all.
If i had to guess, it would be that much of the confusion comes from the thought, that numbers, and their textual representations would either be the same thing, or at least tightly coupled, with some kind of bidirectional binding between them. This is not the case.
The representations like 0.1 and 0.10, which you enter in code, are only used to generate a number. They are convenient names, for what you intend to produce, not the resulting value. In this case, they are names for the same number. It has a lot of other aliases, like 0.100, 1e-1, or 10e-2. In the actual value, there is no contained information, about what or where it came from. The conversion is a one-way street.
When displaying a number as text, by default (Number.prototype.toString), javascript uses an algorithm to construct one of the possible representations from a number. This can only use what's available, the number value, also meaning it will produce the same results for two same numbers. This implies, that 0.1 and 0.10 will produce the same result.
Concerning the number1 value, javascript uses IEEE754-2019 float642. When source code is being evaluated3, and a number literal is encountered, the engine will convert the mathematical value the literal represents to a 64bit value, according to IEEE754-2019. This means any information about the original representation in code is lost4.
There is another problem, which is somewhat unrelated to the main topic. Javascript used to have an octal notation, with a prefix of "0". This means, that 003 is being parsed as an octal, and would throw in strict-mode. Similarly, 010 === 8 (or an error in strict-mode), see Why JavaScript treats a number as octal if it has a leading zero
In conclusion, when trying to keep information about some representation for a number (including leading or trailing zeroes, whether it was written as decimal, hexadecimal, and so on), a number is not a good choice. For how to achieve some specific representation other than the default, which doesn't need access to the originally entered text (e.g. pad to some amount of digits), there are many other questions/articles, some of which were already linked.
[1]: Javascript also has BigInt, but while it uses a different format, the reasoning is completely analogous.
[2]: This is a simplification. Engines are allowed to use other formats internally (and do, e.g. to save space/time), as long as they are guaranteed to behave like an IEEE754-2019 float64 in any regard, when observed from javascript.
[3]: E.g. V8 would convert to bytecode earlier than evaluation, already exchanging the literal. The only relevant thing is, that the information is lost, before we could do anything with it.
[4]: Javascript gives the ability to operate on code itself (e.g. Function.prototype.toString), which i will not discuss here much. Parsing the code yourself, and storing the representation, is an option, but has nothing to do with how number works (you would be operating on code, a string). Also, i don't immediately see any sane reason to do so, over alternatives.

+ operator vs parseFloat

Example 1 of the knockout extenders page describes a way of rounding user input and making sure it is only numeric.
It works great, but looking through the source they do a peculiar thing that i don't understand, that is on line 8 they do this:
parseFloat(+newValue)
newValue is a string.
When i initially asked this question I didn't know what + did - some further poking and a link to a different MDN page from one of the initial answers I got indicate it is a unary operator equivalent to number(str) and that there are some differences between +str and parseFloat(str) (treatment of strings ending in alpha characters and interpretation of hex seem to be the headlines).
I still don't understand why the + in this case needed to be wrapped in the parseFloat although I am starting to think it might be a typo...
Citing MDN docs for parseFloat:
parseFloat parses its argument, a string, and returns a floating point number. If it encounters a character other than a sign (+ or -), numeral (0-9), a decimal point, or an exponent, it returns the value up to that point and ignores that character and all succeeding characters. Leading and trailing spaces are allowed.
Using [unary plus operator][2] you may be sure that `parseFloat` operates on `Number`, which is only useful if you want to be more strict about results but still want to use a `parseFloat`
parseFloat('0.32abcd') // -> 0.32
parseFloat(+'0.32abcd') // -> NaN
**Update:**
After a bit of digging in docs and running some tests, seems there is no reason to use parseFloat other than parsing strings that may contain numbers with non numeric trails to number, eq:
parseFloat('31.5 miles') // -> 31.5
parseFloat('12.75em') // -> 12.75
For any other cases where your string contains number + is a fastest and prefered way (citing MDN docs for unary plus operator):
unary plus is the fastest and preferred way of converting something into a number, because it does not perform any other operations on the number.
See parseFloat versus unary test case for how faster it is.
Previous link broken so here is the new test that shows how unary is faster.

Numbers with leading zeros in Javascript

I receive a long XML from backend. To further use the xml I convert it to JSON object using one of the standard XMLtoJSON javascript library. The issue is, some of the XML value contains number with leading zeros eg: 001072.
The problem is, when javascript library converts xml to JSON, number with leading zeros give completely different value.
For example
“001072” converts “570”
Other times it parse it correctly. For example:
“0045678” converts to 45678
The problem is how javascript handle number with zeros. I don’t know the reason of this strange behavior!!
Please suggest a solution which can parse number with zeros consistently and how can I use it with xmltojson library
This is most likely a problem with octal literals. If a number starts with a leading 0, JavaScript by default will try to parse it as an octal literal.
For this reason, you should always specify the radix parameter when calling parseInt. The library probably does not do that.
parseInt("012", 8); // 10
parseInt("012", 10); // 12
I think this is the offending line in the library, probably. Either edit the library, or edit your XML.
Octal numbers with the leading zero are on the way out. For ECMAScript5 they can still cause problems and are thus not allowed in strict mode and throw a runtime error. You really should not be using 3rd party scripts that are not in strict mode they are dangerous for way too many reasons, as you can see with the handling of the octal numbers.
As ECMAScript 6 becomes more wide spread the use of the leading zero will be pushed out all together.
Octals literals will have a '0o' prefix 0o10 === 8 can be uppercase 'o' but I am sure you can see this will be a hassle. ES6 will also formalise the binary format with the prefix 0b1000 === 8 though most browsers have supported it for some time. Hex has also been around for a while 0x08 == 8
The reason some numbers with leading zeros are decmil and some octal is dependent on what digits are in the number. Octal does not use the digits 8 and 9 so any number that have these digits can not be octal.

using division operator (/) on strings in javascript

I realized that in javascript all 101/100, "101"/100, 101/"100" and "101"/"100" result in 1.01 (checked on Chrome, FF and IE11). But I cannot find a piece of documentation regarding this behaviour.
Therefore my question is if it is (cross-browser) safe to use this feature, and if it is a good practice to do so (or rather to use parseInt before division if the variable can be a string)?
When you use / on strings, the strings are implicitly converted to numbers and then division operation is performed.
This may work in all browsers, but it's always good practice to convert to number explicitly using parseInt or parseFloat or other method.
parseInt("101", 10) / 100
Or
parseFloat("101") / 100
ECMAScript Specifications for Division Operator
Therefore my question is if it is (cross-browser) safe to use this feature...
It depends on your definition of "safe." With the division operator, yes, it's specified behavior: Each operand is converted (implicitly coerced) to a number, and then numeric division is done.
But beware of generalizing this too far. You'll be okay with /, *, and - but it will bite you on +, because if either operand to + is a string, + does string concatenation, not addition.
Another way that it may or may not be "safe" depending on your point of view is the implicit coercion: It uses the browser's JavaScript engine's rules for converting strings to numbers. Some older browsers went beyond the specification (which they were allowed to in the past) and treated numbers starting with a 0 as octal (base 8) rather than decimal. Naturally, end users who type in, say, "0123" as a number probably mean the number 123, not the number 83 (123 in octal = 83 decimal). JavaScript engines are no longer allowed to do that, but some older ones do.
In general, it's probably best to explicitly coerce or convert those operands. Your choices for doing so:
The unary + operator: value = +value will coerce the string to a number using the JavaScript engine's standard rules for that. Any non-digits in the string (other than the e for scientific notation) make the result NaN. Also, +"" is 0, which may not be intuitive.
The Number function: value = Number(value). Does the same thing as +.
The parseInt function, usually with a radix (number base): value = parseInt(value, 10). The downside here is that parseInt converts any number it finds at the beginning of the string but ignores non-digits later in the string, so parseInt("100asdf", 10) is 100, not NaN. As the name implies, parseInt parses only a whole number.
The parseFloat function: value = parseFloat(value). Allows fractional values, and always works in decimal (never octal or hex). Does the same thing parseInt does with garbage at the end of the string, parseFloat("123.34alksdjf") is 123.34.
So, pick your tool to suit your use case. :-)
Type coercion is at play here. Quoting #Barmar's answer from What exactly is Type Coercion in Javascript?
Type coercion means that when the operands of an operator are of different types, one of them will be converted to an "equivalent" value of the other operand's type.
The reason for your observation is valid for other operations too -
1 + "2" will give you "12"
1 - "2" will give you -1
(because "-" operation on strings is not defined like division")
In the case "101/100" the operation "/" will decide the coercion, since there is no operation defined on strings with that operator "/", but is there for "numbers".
Using it is safe (at least in modern browsers) as long as you are clear how type coercion will play out in your operation.

Why parsing a very large number to integer return 1

parseInt(123123123123123123123123); //return 1
parseInt(123123123123123123123123123); //return 1
parseInt(123123123123123123123123123123);//return 1
Test in chrome!
A little creative reading of the documentation for parseInt() provides an answer for this. Here's what's happening:
parseInt expects its first argument to be a string. If it isn't, it converts it to a string. This is actually hilarious, because it appears to do that by...wrapping it in quotes and passing it through .toString(), which is more or less the converse of parseInt() in this case. In your example, parseInt(123123123123123123123123); becomes parseInt("1.2312312312312312e+29").
THEN it takes the converted-to-string value and passes it through parseInt(). As the documentation tells us, if it encounters a non-numeric character, it aborts and goes with what it has so far...and it truncates to an integer. So it's taking "1.2312312312312312e+29", reaching the +, aborting, parsing "1.2312312312312312" instead, and coming up with 1.
Unintended consequences!
You'll only see this problem with ints large enough that when converted to strings, they render in exponential notation. The underlying problem is that even though you'd think parseInt() and Number.toString() would mirror each other...they don't, quite, because int values passed through toString() can generate strings that parseInt() doesn't understand.
First, always specify the radix (second parameter) to avoid guessing.
Second, parseInt expects a string, so add quotes around your number.
parseInt("123123123123123123123123123123", 10)
1.2312312312312312e+29
Mozilla developer reference has good documentation of this function and examples. Regarding the radix, they say:
Always specify this parameter to eliminate reader confusion and to guarantee predictable behavior. Different implementations produce different results when a radix is not specified.

Categories