JSON.Stringify() on big numbers alters the numeric value? - javascript

I have a WCF service operation that returns an object with long and List<string> properties. When I test the operation in a WCF application, everything works fine and the values are correct. However, I need to be able to call the service using jQuery and JSON format. The value of the long property apparently changes when I read it back in the OnSucceed function.
After searching I've found that JSON.stringify changes big values. So in code like this:
alert(JSON.stringify(25001509088465005));
...it will show the value as 25001509088465004.
What is happening?
Demo here: http://jsfiddle.net/naveen/tPKw7/

JavaScript represents numbers using IEEE-754 double-precision (64 bit) format. As I understand it this gives you 53 bits precision, or fifteen to sixteen decimal digits. Your number has more digits than JavaScript can cope with, so you end up with an approximation.
Do you need to do maths operations on this big number? Because if its just some kind of ID you can return it as a string and avoid the problem.

Related

Javascript type conversion between string and number resulting in inconsistent results

I'm working on integrating an Instagram login, authentication and search on a platform, and came across this peculiar problem.
For only this ID that I could find, going both from Integer to String, and the other way around, causes the resulting number to be greater by 1 than the original input. Using the .toString() method from Number to go from Number to String, as well as parseInt() and Number() functions to go from String to Number caused this issue. The specific number is 17841400373590127.
On many different IDs, both of greater and smaller numerical values, I didn't encounter this issue. With this value changing, it causes problems down the line when searching by this ID in our databases and not finding any result.
The ideal solution would be our backend API delivering the same type in all cases, but it would be nice to have the possibility of casting in our Vue frontend without issues like these occurring. Does anyone have an idea why this occurs, and how to possibly avoid it?
On the print screen I demonstrate the problem occurring in chrome's console.
bug demonstration chrome console
As commented, you're getting a inaccurate parsing result because the number in question is greater than JS Number.MAX_SAFE_INTEGER, that is, the greatest number which can be represented and handled with accuracy. If you try to use greater numbers, you'll be subject to getting mathematically incorrect results.
JS also has Number.MIN_SAFE_INTEGER and limits for decimal digits.
If you need to convert an unsafe integer from Number to String, you can use BigInt:
// Here, I'm creating a BigInt by appending "n" to the number.
console.log((17841400373590127n).toString());
Unfotunately, it's impossible to convert such number from String or BigInt to Number and get a correct result.

Why is parseInt() not converting my String of numbers correctly?

I have some logic within a function that takes a string of numbers called digits like so:
6145390195186705543
I then attempt to convert with parseInt() like so:
parseInt(digits)
The result of:
digits = parseInt(digits);
is
6145390195186705000
Can someone help me understand why this is the case? and how i can get an accurate conversion?
This is another version of "broken" floating point math: Javascript uses 64 bits to store numbers as small as the size of an atom up to the number of atoms in the universe. As that is quite a broad range it cannot be stored accurately, therefore the numbers are stored in an imprecise way but can represent a very broad range. In your case 6145390195186705000 is the inaccurate version JS is able to store as 6145390195186705543 cannot be stored.
and how i can get an accurate conversion?
You cannot store an "accurate number", therefore you cannot convert it accurately. However there are some libraries that allow you to work with strings as if they were numbers, such as BigIntJS.
As this is a common problem, this is going to be solved in the next JS version, see this proposal. You can currently test it in the new version of chrome.

JavaScript misinterpretes number

I perform an AJAX call to generate an ID. This ID is sent back to the client in the response and shown in an input field. I was made aware that the ID displayed in the browser is not the one generated - the last digit differs. On the serverside I serialize data to pass it back to the client using Adobe ColdFusion's own serializeJSON() function. It recognizes the sequence of digits and serializes it as a number. I logged the contents of my variables on different places in my codde, it looked fine all the way. Only the browser does not do what I want/expect.
I boiled it down to this simple sample:
var stru = {"MYID":2761602017000540006};
console.dir(stru);
The console logs 2761602017000540000 instead of 2761602017000540006
Why is that? Is this number too large to be stored in JavaScript?
Is the number too large to be stored in JavaScript?
Yes, the max safe integer is 9,007,199,254,740,991 and the number you're attempting to send is 2,761,602,017,000,540,006 (which is a factor of ~1000x larger).
This is because the JavaScript number type follows the IEEE 754 64-bit floating point number format, which doesn't allow as for as large of numbers as a 64-bit integer normally would. You can see the definition of the number type value here in the ECMAScript spec 4.3.20.
I suggest you send the ID over as a String.
In JavaScript, one has at most 53 bits for integers. so you can not put integers larger that 53bits into javascript variables, so the other way is to use strings for saving this long id . I hope that this help you
As Arash said, your number is too long (more than 53 bits).
You can have more information on this topic: Javascript long integer
The only solution seems to be using string instead of numbers

Handling 64-bit numbers using AngularJS' $http.get()

In my Angular app I am making a $http.get() request to a URL that is responding with a json object. This object contains a value that is occasionally a very large number (e.g. 9106524608436223400). Looking at the network profiler in Chrome I can see that the number is coming down properly but when $http.get() has it's callback hit the number will be corrupted somewhat. I assume this is because the number is very large and not a string. Is there any way to get Angular to handle this response correctly or do I need to wrap my server's output as a string? Thanks.
Numbers in JavaScript are double precision floating point numbers. This means that they can only handle integer numbers with full precision up to 52 bits.
Any code for parsing the JSON that will represent the number as a regular number in JavaScript will be unable to give you the unchanged value.
The JSON standard doesn't specify any limitation for the range or precision for numbers. However, as JSON is based on a subset of the JavaScript syntax, one could argue that the format doesn't support numbers outside of what could be represented in JavaScript.
To safely get the value unchanged, you would need to put it as a string in the JSON, or split it up into two or more smaller numbers.

JavaScript 64 bit numeric precision

Is there a way to represent a number with higher than 53-bit precision in JavaScript? In other words, is there a way to represent 64-bit precision number?
I am trying to implement some logic in which each bit of a 64-bit number represents something. I lose the lower significant bits when I try to set bits higher than 2^53.
Math.pow(2,53) + Math.pow(2,0) == Math.pow(2,53)
Is there a way to implement a custom library or something to achieve this?
Google's Closure library has goog.math.Long for this purpose.
The GWT team have added a long emulation support so java longs really hold 64 bits. Do you want 64 bit floats or whole numbers ?
I'd just use either an array of integers or a string.
The numbers in javascript are doubles, I think there is a rounding error involved in your equation.
Perhaps I should have added some technical detail. Basically the GWT long emulation uses a tuple of two numbers, the first holding the high 32 bits and the second the low 32 bits of the 64 bit long.
The library of course contains methods to add stuff like adding two "longs" and getting a "long" result. Within your GWT Java code it just looks like two regular longs - one doesn't need to fiddle or be aware of the tuple. By using this approach GWT avoids the problem you're probably alluding to, namely "longs" dropping the lower bits of precision which isn't acceptable in many cases.
Whilst floats are by definition imprecise / approximations of a value, a whole number like a long isn't. GWT always holds a 64 bit long - maths using such longs never use precision. The exception to this is overflows but that accurately matches what occurs in Java etc when you add two very large long values which require more than 64 bits - eg 2^32-1 + 2^32-1.
To do the same for floating point numbers will require a similar approach. You will need to have a library that uses a tuple.
The following code might work for you; I haven't tested it however yet:
BigDecimal for JavaScript
Yes, 11 bit are reserved for exponent, only 52 bits containt value also called fraction.
Javascript allows bitwise operations on numbers but only first 32 bits are used in those operations according to Javascript standard specification.
I do not understand misleading GWT/Java/long answers in Javascript/double question though? Javascript is not Java.
Why would anyone need 64 bit precision in javascript ?
Longs sometimes hold ID of stuff in a DB so its important not to lose some of the lower bits... but floating point numbers are most of the time used for calculations. To use floats to hold monetary or similar exacting values is plain wrong. If you truely need 64 bit precision do the maths on the server where its faster and so on.

Categories

Resources