question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Assign -1 to BigInt, but BigInt return 18446744073709552000

See original GitHub issue

Hi, JSBI is a very good API for me, but I am confused in following case.

In offical web site say

Another thing to note is that the >>> operator, which performs an unsigned right shift, does not make sense for BigInts since they’re always signed. For this reason, >>> does not work for BigInts.

So, I assume the following code should have same result, why they are different?

let max = JSBI.BigInt("0xffffffffffffffff");
console.log(`${JSBI.toNumber(max)} ==? ${max.toString(16)}`);
let num_m1 = JSBI.BigInt("-1");
console.log(`${JSBI.toNumber(num_m1)} ==? ${max.toString(16)}`);

results:

[Log] 18446744073709552000 ==? ffffffffffffffff (main.chunk.js, line 1293)
[Log] -1 ==? ffffffffffffffff (main.chunk.js, line 1295)

Very thanks.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
jakobkummerowcommented, Aug 14, 2019

I think this is all working as intended.

JSBI.toNumber(max) performs a to-Number conversion (as the name implies). 0xFFFFFFFFFFFFFFFF cannot be represented as a JavaScript Number, because a 64-bit double doesn’t have enough mantissa bits. So the number you get out is rounded up to 0x10000000000000000. Since the default Number-to-String conversion (which is invoked here under the hood for printing) uses base-10, it shows up in your output as 18446744073709552000.

In your second comparison, on the left side the JSBI representation of -1 gets converted to the Number representation of -1, and on the right side you again have the base-16 String representation of max.

It looks like what you’re looking for is indeed asIntN: JSBI.asIntN(64, max).toString() gives you -1.

Note that “max” is a misnomer: this is far from the maximum BigInt. “num_m1” is also a misnomer because a JSBI instance (or native BigInt) is not a Number.

FWIW, all of the above is exactly the same between JSBI and native BigInts:

> let max = 0xffffffffffffffffn
> console.log(`${Number(max)} ==? ${max.toString(16)}`)
18446744073709552000 ==? ffffffffffffffff
> let num_m1 = -1n
> console.log(`${Number(num_m1)} ==? ${max.toString(16)}`)
-1 ==? ffffffffffffffff

In summary, there are two things to keep in mind:

  • converting a (native or JSBI) BigInt to Number typically changes the value by losing precision (that’s why BigInts have been added to JavaScript: they are not the same as Numbers). Most BigInt code should not contain any toNumber conversions.
  • the idea that “-1” is represented internally as 0xFFFF… does not generally apply to BigInts (although some operations pretend that this is the case, notably asIntN and the bitwise operations; and some implementations might choose to actually do it that way). FWIW, it also doesn’t apply to floating-point numbers, JavaScript is just really confusing there because it sometimes says that all Numbers are doubles and sometimes pretends that all Numbers are 32-bit signed integers.
1reaction
jakobkummerowcommented, Aug 14, 2019

new functions asInt64 and asUInt64

These functions do not exist; there are only the generic asIntN and asUintN. You can call them as asIntN(64, ...) or asIntN(65, ...) or asIntN(13, ...), or any other N you want.

I guess this lib try to support variable bits BigInt

JSBI supports BigInts with any number of bits (from 0 up to some huge maximum, depending on the JavaScript engine you run it on, typically millions or even billions).

the lib should know which bit is signed/unsigned flag

In general, no bit in a BigInt is “the signed/unsigned flag”. You can assume that the sign is stored separately from the absolute value. (Whether that’s actually the case in an implementation is an irrelevant implementation detail; conceptually it is always the case.) Only the asIntN(N, ...) function pretends that the Nth bit is the sign bit. A library cannot know what you’re going to pass as N.

If the lib tries to support variable bits BigInt, it should support unsigned right shift

As the README says, JSBI implements the same functionality as native JavaScript BigInts. Since native BigInts have no unsigned right shift, neither does JSBI. See https://tc39.es/proposal-bigint/.

Unsigned right-shift, also known as zero-extending right shift, really only makes sense when you know that (1) the underlying representation has a fixed and well-known size, and (2) the system you’re running on uses two’s-complement representation for negative values. Neither is the case for BigInts.

Example: in C/C++ on common hardware, int8_t x = -8 is represented with the bits 11111000. Doing a zero-extending right shift by 2 bits gives 00111110, which is 62 in decimal. If you started with the same value -8 but stored as a 16-bit value int16_t y = -8, you’d get 1111111111111000b >> 2 == 0011111111111110b, which is 16382 in decimal. The width of the variable matters. Now if you had a BigInt with value -8, what width would you assume? How many 1-bits should there be? There is no good answer to this, which means there is no good way to define what a “zero-extending right shift” should do for negative BigInts. (For positive BigInts it’s the same as sign-extending right shift anyway.)

I am also a little confused in the name “signedRightShift”.

It is the name used in the BigInt proposal and other places; MDN calls it “sign-propagating right shift”. Just think of the >> operator. The key point is that negative values remain negative when they are right-shifted. This creates nice symmetry with positive values:

4 >> 1 === 2
-4 >> 1 === -2

(although do note that rounding is different: 5 >> 1 == 2 but -5 >> 1 == -3). And this is exactly what both JSBI and native BigInts also do: -4n >> 1n === -2n, JSBI.signedRightShift(JSBI.BigInt(-4), JSBI.BigInt(1)).toString() === "-2"

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why didn't scala design around Integer Overflow?
In particular it has a SafeLong type that handles arbitrary-precision integers but with much better performance than BigInt for values which ...
Read more >
TypeError: can't convert x to BigInt - JavaScript - MDN Web Docs
The JavaScript exception "x can't be converted to BigInt" occurs when attempting to convert a Symbol, null, or undefined value to a BigInt, ......
Read more >
PGRES_FATAL_ERROR:ERROR: bigint out of range ...
The current value returned is -1 but Zabbix is still trying to insert this as an unsigned integer (18446744073709552000) which is 20 digits...
Read more >
[SOLVED] MySQL unsigned BIGINT issues [Archive]
I know that mixing of signed and unsigned values requires some attention from the programmers side. But in this case, the MySql server...
Read more >
SQLite, 64-bit integers, and the impossible number
1 2 3, CREATE TABLE big_numbers (i INTEGER, r REAL, t TEXT, b BLOB); INSERT INTO big_numbers ... so let's see what types...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found