Search code examples
javascriptbit-manipulationbitwise-operatorsbit-shiftbitwise-and

Bitwise OR operation with 0b transform a given number in negative


I need to perform certain operations with bitwise operators, let's say we have this 40 bit unsigned integer:

1071698660929 when I apply to it a OR operator and an unsigned right shift operator I got this negative integer

Input: (1071698660929 >>> 0) | 0b0

Output: -2043163071

I noticed that as long as the number is 31 bit long I don't have any problem adding a 0B0 but when the number is greater than 31 bit it becomes negative even though I'm using the unsigned right shift operator:

32 bits binary return -1: (0b11111111111111111111111111111111 >>> 0) | 0

31 bits binary return 2147483647 : (0b1111111111111111111111111111111 >>> 0) | 0

So I have a couple of questions:

  1. Why the numbers above are becoming negative if I'm using a right shift operator? What I understand is that the MAX safe integer in javascript is 53 bit long Number.MAX_SAFE_INTEGER.toString(2).length so I'm within the integer safe boundaries.
  2. What could I do to make this (1071698660929 >>> 0) | 0b0 returns 1071698660929 instead of -2043163071?

Solution

  • Bitwise operators implicitly convert their operands to 32-bit integers (signed or unsigned, depending on operator). In process, numbers with more than 32 bits get their most significant bits discarded:

    Before: 11100110111110100000000000000110000000000001
    After:              10100000000000000110000000000001
    

    The only exception of this rule is (relatively) new kid on the primitive block, BigInt type: all the bitwise ops (&, |, ^) and almost all the shifts are overloaded for that type.

    This opens a way out of your problem: just use BigInt instead of plain number, either explicitly, like this:

    const bigIntee = BigInt(1071698660929); // note no `new` here!
    

    ... or, if you actually have to use literal, by appending n to it:

    const bigIntee = 1071698660929n; // also works
    

    Note that if one operand is BigInt, another should be BigInt as well. So this...

    bigIntee | 0
    

    ... will make JS angry with your pathetic attempts of getting back to happy type-anarchy times of 90s. To be more specific, you'll get a TypeError thrown right at you:

    Cannot mix BigInt and other types, use explicit conversions
    

    This is easily fixable, though: just apply either of aforementioned conversion tricks to the second operand as well.

    bigIntee | 0n 
    // or bigIntee | BigInt(0)
    

    Both works. Disregard 0 being, well, not really a Biggus Integerus kind; type safety reigns here.

    Still, there's a problem: >>> (unsigned right shift operator) cannot be used on BigInts. If you attempt to go with it, you'll get a pretty specific error:

    bigIntee >>> 0n
    // Uncaught TypeError: BigInts have no unsigned right shift, use >> instead
    

    ... which makes total sense (and us wishing all the errors thrown by JS were that specific).