Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 289
Simon Avatar asked May 17 '12 07:05

Simon


2 Answers

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.

like image 123
Salman A Avatar answered Nov 12 '22 10:11

Salman A


The number is too big to be parsed correctly.

One solution is:

  1. Preprocessing your string from API to convert it into string before parsing.
  2. Preform normal parsing
  3. 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
like image 34
Đỗ Ngọc Hoan Avatar answered Nov 12 '22 08:11

Đỗ Ngọc Hoan