JSON transfer of bigint: 12000000000002539 is converted to 12000000000002540? - javascript

I'm transferring raw data like [{id: 12000000000002539, Name: "Some Name"}] and I'm getting the object [{id: 12000000000002540, Name: "Some Name"}] after parsing, for now server side converting id into string seems to help.
But is there a better way to transfer bigint data correctly?

The value is actually not exceeding the maximum numeric value in JavaScript (which is "only" 1.7308 or so).
However, the value is exceeding the range of "integral precision". It is not that the wrong number is sent: rather, it is that the literal 12000000000002539 can only be represented as precisely as 12000000000002540, and thus there was never the correct numeric value in JavaScript. (The range of integrals is about +/- 253.)
This is an interesting phenomena of using a double relative-precision (binary64 in IEEE-754 speak) type to store all numeric values, including integers:
12000000000002539 === 12000000000002540 // true
The maximum significant number of decimal digits that be precisely stored as a numeric value is 15 (15.95, really). In the above, there are 17 significant digits, so some of the least-significant information is silently lost. In this case, as the JavaScript parser/engine reads in the literal value.
The only safe way to handle integral numbers of this magnitude in JavaScript is to use a string literal or to break it down in another fashion (e.g. a custom numeric type or a "bigint library"). However, I recommend just using a string, as it is human readable, relatively compact (only two extra characters in JSON), and doesn't require special serialization. Since the value is just an "id" in this case, I hope that math does not need to be performed upon it :)
Happy coding.

Related

parseFloat stripping last digits and converting to zeros

I have a scenario where I need to parsefloat 19 digit string to number.
e.g. parseFloat("1000000000100000043") gives me 1000000000100000000
but the expected output required is 1000000000100000043
This is likely a precision overflow error.
The Number data type (but also int and float in other languages) have a finite number of bits available to represent a number. Typically around 15-16 decimal digits worth.
When length of original number in the string exceeds available precision, such number can no longer be represented by the target data type.
In this case the parseFloat function fails silently. If you want to catch this situation you need to add code to check incoming data or use another function, possibly a custom one.
Alternatively, you can convert the numeric value back to string and compare it with original to detect a discrepancy.
See also a question regarding double.Parse
You are running into how Javascript numbers are stored. See, e.g., here: https://www.w3schools.com/js/js_numbers.asp
You can use a library like decimal.js to work with large, exact numbers. These libraries store the number as string, but allow you to do mathematical operations.

Why does a number get its last 2 digits turned to 0?

Use
Hi, so I was working on my discord bot with some user ids that I stored in a database, which then I will get back to ping them or give them roles.
Problem
Though here's the problem, in the database they get turned from ex: 533692905387196429 to 533692905387196400. I tried setting that to a String, and it worked, in the database, it's stored fully how it's supposed to be. But, when I get them back from the database and turn them into a number or an integer they get turned back to ex: 533692905387196400.
Tried using
I tried using parseInt(), parseFloat() and Number() but all of them give the same result.
More info
Also if I remove 2 digits for example: 533692905387196429I remove 19 from there, it will give back 533692905387(19)6429 instead of 533692905387196429 and instead of 533692905387196400.
Any help is appreciated!
So, Number.MAX_SAFE_INTEGER is 9007199254740991 (which is 253 - 1). Your number is too large to hold full precision.
If you want a number with that level of digits and precision, you can use BigInt and then you will have to be very careful how you use that BigInt as it cannot be directly used in place of a regular Number type, but it can hold infinite precision and you can do math between two BigInt values.
Whether it's best used as a BigInt or as a String really depends upon what you're doing with it. If these are just some sort of ID that you aren't actually doing numeric operations with, then you can just keep it as a string.
The number is too big, so Javascript doesn't keep full precision!
> 533692905387196429
533692905387196400
You can resolve this by storing them as strings:
> '533692905387196429'
'533692905387196429'
You shouldn't need to do any mathematical operations with Discord IDs so there shouldn't be any issue storing and treating them as strings everywhere.

how to handle more than 20 digit number (Big integer)?

My angular program, I need to pass the number which is more than 20 digit to the API request.
num: any;
this.num = 2019111122001424290521878689;
console.log(this.num); // It displays "2.0191111220014244e+27"
I tried to change string from number as below
console.log(this.num.toString()); // It displays "2.0191111220014244e+27"
My expectation is that I need to pass the original big integer into the API request. If I pass as below, it goes as "2.0191111220014244e+27".
BTW, I tried BigInt(this.num), which gives difference number.
Suggest me
In JavaScript, big integer literals have the letter n as a suffix:
var bigNum = 2019111122001424290521878689n;
console.log(bigNum);
For more information, see
MDN JavaScript Reference - BigInt
If you got a large number (> SAFE_INTEGER) from an API, in JSON format, and you want to get the exact value, as as string, you unfortunately can't use JSON.parse(), as it will use the number type and lose precision.
There are alternative JSON parsers out there like LosslessJSON that might solve your problem.
You can use BigInt.
BigInt is a built-in object that provides a way to represent whole numbers larger than 253 - 1, which is the largest number JavaScript can reliably represent with the Number primitive. BigInt can be used for arbitrarily large integers.
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n
const hugeString = BigInt("9007199254740991");
// ↪ 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// ↪ 9007199254740991n
const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
// ↪ 9007199254740991n
BigInt is similar to Number in some ways, but also differs in a few key matters — it cannot be used with methods in the built-in Math object and cannot be mixed with instances of Number in operations; they must be coerced to the same type. Be careful coercing values back and forth, however, as the precision of a BigInt may be lost when it is coerced to a Number.
Refer to
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
The problem is that the number you have there is not an integer. Javascript can only store integers up to the value given by Number.MAX_SAFE_INTEGER. In chrome, this number is 9007199254740991.
The number you have is actually a floating point number, and converting it between floating point and integer will loose some precision.

Reassembling negative Python marshal int's into Javascript numbers

I'm writing a client-side Python bytecode interpreter in Javascript (specifically Typescript) for a class project. Parsing the bytecode was going fine until I tried out a negative number.
In Python, marshal.dumps(2) gives 'i\x02\x00\x00\x00' and marshal.dumps(-2) gives 'i\xfe\xff\xff\xff'. This makes sense as Python represents integers using two's complement with at least 32 bits of precision.
In my Typescript code, I use the equivalent of Node.js's Buffer class (via a library called BrowserFS, instead of ArrayBuffers and etc.) to read the data. When I see the character 'i' (i.e. buffer.readUInt8(offset) == 105, signalling that the next thing is an int), I then call readInt32LE on the next offset to read a little-endian signed long (4 bytes). This works fine for positive numbers but not for negative numbers: for 1 I get '1', but for '-1' I get something like '-272777233'.
I guess that Javascript represents numbers in 64-bit (floating point?). So, it seems like the following should work:
var longval = buffer.readInt32LE(offset); // reads a 4-byte long, gives -272777233
var low32Bits = longval & 0xffff0000; //take the little endian 'most significant' 32 bits
var newval = ~low32Bits + 1; //invert the bits and add 1 to negate the original value
//but now newval = 272826368 instead of -2
I've tried a lot of different things and I've been stuck on this for days. I can't figure out how to recover the original value of the Python integer from the binary marshal string using Javascript/Typescript. Also I think I deeply misunderstand how bits work. Any thoughts would be appreciated here.
Some more specific questions might be:
Why would buffer.readInt32LE work for positive ints but not negative?
Am I using the correct method to get the 'most significant' or 'lowest' 32 bits (i.e. does & 0xffff0000 work how I think it does?)
Separate but related: in an actual 'long' number (i.e. longer than '-2'), I think there is a sign bit and a magnitude, and I think this information is stored in the 'highest' 2 bits of the number (i.e. at number & 0x000000ff?) -- is this the correct way of thinking about this?
The sequence ef bf bd is the UTF-8 sequence for the "Unicode replacement character", which Unicode encoders use to represent invalid encodings.
It sounds like whatever method you're using to download the data is getting accidentally run through a UTF-8 decoder and corrupting the raw datastream. Be sure you're using blob instead of text, or whatever the equivalent is for the way you're downloading the bytecode.
This got messed up only for negative values because positive values are within the normal mapping space of UTF-8 and thus get translated 1:1 from the original byte stream.

How to round off 9999999999999999 to 9999999999999998

In my case, i am converting a string value of '9999999999999999' to integer using parseFloat(). But it converts to next number of it i.e. 10000000000000000. But i need to convert it to before of that number i.e. 999999999999999998. I have searched for a while in google. But could not get clear idea to implement this.
Try this
document.getElementById("demo").innerHTML=Math.round(9999999999999999-2);
OUTPUT
9999999999999998
This number is too big to represented precisely in JavaScript Number value. So no amount of conversion will give you values reliably/precisly as you want around such range.
I.e. (9999999999999999-1)===(9999999999999999) returns true, but (9999999999999998)===(9999999999999999) returns false.
If you need such high precision in JavaScript (similar to many other languages) you need to use specialized data types (unfortunately there is no "BigInteger" type built in in JavaScript).
You will need to use some external javascript library to work with big numbers like that, cause max number you cant represent without losing presicion in javascript integers is 9007199254740992 (Explanation : What is JavaScript's highest integer value that a Number can go to without losing precision?)
Here you have some link where people discuss about some libraries to use for javascript big numbers.
How to deal with big numbers in javascript

Categories

Resources