JavaScript JSON.parse string bug - convert value to Infinity - javascript

Can anyone explain me this strange behavior in JSON.parse() function in Javascript?
When calling it with string, it should raise an error.
e.g
JSON.parse("5ffc58ed1662010012d45b30");
result with:
VM230:1 Uncaught SyntaxError: Unexpected token f in JSON at position 1
at JSON.parse (<anonymous>)
at <anonymous>:1:6
...
BUT!!
When I call it with this specific value:
JSON.parse("60000528880e130012727947");
It return Infinity??? Why??? How this possible? What so special in this string?
Is this because this string is entirely consisting numbers and e in the middle? so JSON.parse thinks it's a kind of float?

JSON is a text representation of same data, usually a structure like an array or an object but a primitive value like a string or a number can also be represented as JSON without problems.
Being a text, in the source code it is represented as a JavaScript string.
The line:
JSON.parse("60000528880e130012727947");
can be as well JSON.parse(x), where x is a variable that contains the JSON.
The JSON in the example above is exactly this: 60000528880e130012727947 (there are no quotes around it, the quotes are the way a text is represented in the JavaScript source code). It is the text representation of a real number, 60,000,528,880 * 10^130,012,727,947, to be more precise.
JavaScript uses double-precision 64-bit binary format IEEE 754 to represent the number. The largest value a Number can hold is about 1.8×10^308 which is very much for most practical purposes. However, it is a small value compared to the value you have represented as JSON.
Anyway, no matter how large it is, since the value stored as JSON is larger than the greatest value that can be represented using the 64-bit double-precision format, Infinity is used instead.
Regarding the other example, 5ffc58ed1662010012d45b30 is not a valid representation of a number, therefore the JSON parser throws an error when it reaches the first f character at index 1.
All in all, JSON.parse() works fine, your input is not always valid JSON.

Basically it is considering the second string as a big number

Related

JSON.stringify() converts integers to exponential

I have a record
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1000000000000000000000
],
JSON.stringify() converts it to the form
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1e+21
],
JSON.stringify() writes it accordingly the same way, can this be somehow solved?
JSON.parse doesn't convert it to 1e+21, it converts it to a number that, when converted to string in the usual way, is output as the string "1e+21". But the number is the same number whether you write it as 1000000000000000000000 or 1e+21.
JSON.stringify may output it in either form; both are valid JSON numbers, and both define exactly the same number.
I should note that you need to beware of numbers of that magnitude in JavaScript (or any other language that uses IEEE-754 double-precision floating point numbers [or single-precision ones, actually]). That number is well into the range where even integers may be imprecisely represented. Any number greater than 9,007,199,254,740,992 (Number.MAX_SAFE_INTEGER + 1) may or may not have a precise representation. It happens that 10,000,00,000,000,000,000,000 (your number) does, but for instance, 9,007,199,254,740,993 doesn't, nor do any odd numbers from that point upward. At some point, you get to where only multiples of 4 can be represented; and then later it's only multiple of 8, etc. See this question's answers for more.
If you still need to get 1e+21 as 1000000000000000000000, you can use (1e+21).toLocaleString().split(',').join('')
but actually, you don't need to convert it if you want to use it as a number, because they are absolutely the same.
Instead, you can keep the number as string and use +'1000000000000000000000' or parseInt('1000000000000000000000') when you need to use it as a number.

What does passing an integer number as the argument for `toString` do?

I'm taking a course on JavaScript but have no guide on toString method, what's the purpose of these two outputs in JavaScript:
(35).toString(36) >>> "z"!!!
(35).toString(37) >>> throws a RangeError!!!
I am utterly confused as to why I am getting these results, I would really appreciate it if someone could shed some light on this.
tldr: Number.prototype.toString can take an argument called radix that specifies the numeric base to use for the string representation of the number in question.
Object.prototype.toString()
An object's toString method is supposed to return a string representation of that object (I say "supposed" because you can modify it so it doesn't return a string). 35 is clearly not an object, it is a primitive, but you are using it like an object, which causes JavaScript to create a temporary Number object for that toString call (see this StackOverflow answer on autoboxing).
Number.prototype.toString()
About the confusing behavior you are getting by passing 36 to (35).toString: it is because Number.prototype.toString can take an argument that you can use to specify the numeric base to use for the string representation of that number, the argument must be an integer (or any other value that can be coerced to an integer, e.g 35..toString([20])) between 2 and 36, so 2 <= [radix] <= 36 (this means your second example will throw a RangeError).
So, when you execute (35).toString(36), 2 things happen (not necessarily in my order, and most likely it is done in a single step, 35 ====> [string representation of 35 in numeric format specified by "radix"]):
Generate a string representation of the number 35.
Convert the string generated in step #1 to the number base specified by radix.
For example, if you wanted a string representation of 35, in binary form:
console.log(35..toString(2)); // "100011"
Fun fact: the syntax [integer]..[method] is totally valid in JavaScript, the first . is interpreted as a decimal point, the latter as the . that precedes the name of an object's method/property.
Radix
If you don't know what a "radix" is (because I didn't prior to this question, and no, I am no caveman, English is not my native language), here is the definition I got by a Google search for "radix meaning":
the base of a system of numeration.
toString converts an Int to a string. An int is used for math and a string is used as text and math should not be done on it.
Maybe a little more of the code you are looking at would shed more light on what is going on in the script

Parse big binary strings to base 10

I'm having some trouble converting big binary strings to base 10. parseInt(string, 2) should return the int, but when using big strings (1800 characters) it maxes out the variable and just returns Infinity. How can get around that?
A 1800 bit binary number would be well over the maximum possible number value in JavaScript. A regular number datatype will not be able to hold that value, and so JavaScript just calls it Infinity. If you need arbitrarily large numbers, you will have to use some bignum library and probably write a custom string to number function.

Unexpected behavior adding stringified integer to decimal in js

I had a strange bug that I just found the source of in my code.
"1" + .88 // 10.88
What's going on here?
When adding a number and a string (whatever their order), the number is converted to a string and then the two are concatenated.
.88.toString()
is
"0.88"
So you get the string
"10.88"
which appears as
10.88
in most contexts (for example in an HTML input).
If you want a specification based analysis, it starts here with
Then the number to string conversion with the leading 0.is described here:
(s=88, k=2, n=0)

JSON.parse parses / converts big numbers incorrectly

My problem is really simple but I'm not sure if there's a "native" solution using JSON.parse.
I receive this string from an API :
{ "key" : -922271061845347495 }
When I'm using JSON.parse on this string, it turns into this object:
{ "key" : -922271061845347500 }
As you can see, the parsing stops when the number is too long (you can check this behavior here). It has only 15 exact digits, the last one is rounded and those after are set to 0. Is there a "native" solution to keep the exact value ? (it's an ID so I can't round it)
I know I can use regex to solve this problem but I'd prefer to use a "native" method if it exists.
Your assumption that the parsing stops after certain digits is incorrect.
It says here:
In JavaScript all numbers are floating-point numbers. JavaScript uses
the standard 8 byte IEEE floating-point numeric format, which means
the range is from:
±1.7976931348623157 x 10308 - very large, and ±5 x 10-324 - very small.
As JavaScript uses floating-point numbers the accuracy is only assured
for integers between: -9007199254740992 (-253) and 9007199254740992
(253)
You number lies outside the "accurate" range hence it is converted to the nearest representation of the JavaScript number. Any attempt to evaluate this number (using JSON.parse, eval, parseInt) will cause data loss. I therefore recommend that you pass the key as a string. If you do not control the API, file a feature request.
The number is too big to be parsed correctly.
One solution is:
Preprocessing your string from API to convert it into string before parsing.
Preform normal parsing
Optionally, you could convert it back into number for your own purpose.
Here is the RegExp to convert all numbers in your string (proceeded with :) into strings:
// convert all number fields into strings to maintain precision
// : 922271061845347495, => : "922271061845347495",
stringFromApi = stringFromApi.replace(/:\s*(-?\d+),/g, ': "$1",');
Regex explanation:
\s* any number of spaces
-? one or zero '-' symbols (negative number support)
\d+ one or more digits
(...) will be put in the $1 variable

Categories