OrdinaryOrange
OrdinaryOrange

Reputation: 2712

BigInt equivalent to long.js

I'm looking at some code that uses long.js and it uses the constructor

Long(low: number, high?: number, unsigned?: boolean)

I'm trying to replace it with an equivalent that uses BigInt. I can get it to work when unsigned=true but not when false

var upper = -1234
var lower = -11
var pad = '00000000000000000000000000000000'
let bin = function (a) { return (pad + (a >>> 0).toString(2)).slice(-32) }
var bi = BigInt('0b' + bin(upper) + bin(lower))

If I pass in upper & lower & false to long.js the numbers don't agree and I cant get my head around it.

Upvotes: 0

Views: 1127

Answers (1)

jmrk
jmrk

Reputation: 40661

Let's look at how that constructor is defined (skipping comments for brevity):

function Long(low, high, unsigned) {
  this.low = low | 0;
  this.high = high | 0;
  this.unsigned = !!unsigned;
}

That's straightforward to translate to BigInt operations:

  • ... | 0 performs a truncation to 32 bits, which for BigInts can be expressed as ... & 0xFFFFFFFFn. There are also the two helper functions BigInt.asUintN(32, ...) and BigInt.asIntN(32, ...); while the latter more closely reflects what ... | 0 does for Numbers, we want to use the former since we'll shift the results around (rule of thumb: for bit fiddling operations like masking and shifting you almost always want to use unsigned interpretations, the one exception is when you need a sign-extending right-shift).
  • The Long class implicitly considers the low and high value as the two halves of a 64-bit integer; for the BigInt equivalent we'll assemble this integer explicitly. So we get:
function BigLong(low, high, unsigned) {
  low = BigInt.asUintN(32, BigInt(low));
  high = BigInt.asUintN(32, BigInt(high));
  var combined = (high << 32n) | low;
  return unsigned ? BigInt.asUintN(64, combined)
                  : BigInt.asIntN(64, combined);
}

(If you wanted to somehow save the approach of going via a string representation of the value's binary encoding, you'd have to come up with a way to force signed interpretation of the bit string. One possibility that comes to mind is a subtraction, i.e. extend your snippet with:

if (!unsigned) bi -= 2n ** 64n;

But the string detour is still less efficient and IMHO less readable than using BigInt operations.)

Upvotes: 1

Related Questions