Bitwise Operations in JavaScript

JavaScript code on dark editor screen, bitwise operations tutorial

JavaScript's bitwise operators work on 32-bit signed integers behind the scenes. This guide covers all six operators, the quirks of the 32-bit conversion, when to use BigInt, real patterns for flags and color parsing, and when bits beat Math.

JavaScript code with bitwise operators on a dark editor
The Six Operators The 32-Bit Trap BigInt Operations Practical Patterns FAQ

The Six Bitwise Operators in JavaScript

JavaScript gives you the same six operators as C or Java: & (AND), | (OR), ^ (XOR), ~ (NOT), << (left shift), >> (signed right shift), and >>> (unsigned right shift). The last one is JavaScript-specific — most C-family languages do not have it as a distinct operator.

const a = 0b1100;  // 12
const b = 0b1010;  // 10

console.log(a & b);   // 8  (0b1000)
console.log(a | b);   // 14 (0b1110)
console.log(a ^ b);   // 6  (0b0110)
console.log(~a);      // -13 (flips all 32 bits)
console.log(a << 1);  // 24
console.log(a >> 1);  // 6
console.log(a >>> 1); // 6 — same for positive numbers

Each operator converts its operands to 32-bit signed integers, does the operation, and converts back to a JavaScript Number (64-bit float). This is the key thing to understand before using them in production code.

The 32-Bit Integer Trap

JavaScript numbers are 64-bit IEEE 754 floats under the hood. But bitwise operators do something surprising: they first call ToInt32() on each operand, converting it to a 32-bit signed integer. This means any bits beyond position 31 are discarded, and fractional parts are chopped off.

// Surprising truncation
const big = 0xFFFFFFFF + 1;  // 4294967296 — fits in a JS Number
console.log(big | 0);         // 0 — because only the low 32 bits survive

// Fractional chop
console.log(3.7 | 0);   // 3 — used as a fast floor() by some devs
console.log(-3.7 | 0);  // -3

// Values outside 32-bit signed range wrap around
console.log(2147483648 | 0);  // -2147483648 (wraps to INT32_MIN)

I ran into this when building a hex color parser. A 24-bit color value is safe, but anything using bits beyond position 31 silently breaks. The fix is BigInt, which I cover next.

Signed Right Shift vs. Unsigned Right Shift

JavaScript has two right shift operators. >> copies the sign bit into the leftmost positions, preserving the sign. >>> always fills with zeros. With negative numbers, these give wildly different results:

const neg = -8;
console.log(neg >> 2);   // -2  — sign preserved
console.log(neg >>> 2);  // 1073741822 — treated as unsigned 32-bit

// Why? -8 as 32-bit unsigned is 0xFFFFFFF8
// Shifting right by 2 with zero-fill gives 0x3FFFFFFE = 1073741822

BigInt: 64-Bit and Beyond

ES2020 introduced BigInt, which supports arbitrary-precision integers and bitwise operations on them. The syntax is nearly identical — just append n to your literals. BigInt bitwise ops do not truncate to 32 bits.

// BigInt bitwise operations — no 32-bit limit
const bigA = 0xABCD1234ABCD1234n;
const bigB = 0xFFFF0000FFFF0000n;

console.log((bigA & bigB).toString(16));  // "abcd0000abcd0000"
console.log((bigA | bigB).toString(16));  // "ffff1234ffff1234"
console.log((bigA << 32n).toString(16));  // shifts work at full precision

// But: BigInt and Number do not mix
// const x = 5 & 3n;  // TypeError!

Note that BigInt does not support >>>. Signed right shift is the only option, and since BigInt has no fixed width, there is no concept of an unsigned shift anyway. When you need the unsigned behavior, mask after shifting: (n >> k) & mask.

Patterns I Use in Real JavaScript Code

Feature Flags with a Single Integer

Instead of passing multiple booleans to a function, pack them into one integer. Each bit is a flag. This is how the DOM's Node.compareDocumentPosition() and many Web APIs work.

const FLAG_READ  = 1 << 0;  // 1
const FLAG_WRITE = 1 << 1;  // 2
const FLAG_ADMIN = 1 << 2;  // 4

let permissions = 0;
permissions |= FLAG_READ | FLAG_WRITE;  // 3

// Check a flag
const canWrite = (permissions & FLAG_WRITE) !== 0;  // true

// Toggle admin
permissions ^= FLAG_ADMIN;  // adds admin (3 | 4 = 7)
permissions ^= FLAG_ADMIN;  // removes admin (7 ^ 4 = 3)

Hex Color Parsing

Converting a hex color string to RGB channels is a two-step process: parse the hex, then extract each channel with shifts and AND masks.

function hexToRgb(hex) {
    const n = parseInt(hex.replace("#", ""), 16);
    return {
        r: (n >> 16) & 0xFF,
        g: (n >> 8) & 0xFF,
        b: n & 0xFF
    };
}

const { r, g, b } = hexToRgb("#4A90D9");
console.log(`R:${r} G:${g} B:${b}`);  // R:74 G:144 B:217

Fast Parity and Even/Odd Checks

n & 1 is the fastest way to check if a number is odd. It is a single CPU instruction versus n % 2 which does division. For hot loops processing thousands of values, this adds up.

const values = [3, 7, 12, 9, 4, 8, 15];

// Separate evens and odds in one pass
const evens = [];
const odds = [];
for (const v of values) {
    (v & 1 ? odds : evens).push(v);
}
console.log(evens);  // [12, 4, 8]
console.log(odds);   // [3, 7, 9, 15]

Checking Powers of Two

A non-zero number is a power of two exactly when it has a single bit set. The test (n & (n - 1)) === 0 checks this in O(1). This pops up all the time — canvas sizing, buffer allocations, bitmask validation.

function isPowerOfTwo(n) {
    return n > 0 && (n & (n - 1)) === 0;
}

console.log(isPowerOfTwo(64));   // true
console.log(isPowerOfTwo(100));  // false
console.log(isPowerOfTwo(0));    // false — edge case handled

Frequently Asked Questions

Why do JavaScript bitwise operators work on 32-bit integers?

JavaScript's bitwise operators convert operands to 32-bit signed integers before operating. This comes from the ECMAScript specification, which chose 32-bit for consistency with common CPU register sizes. Numbers larger than 2^31-1 or with fractional parts get truncated. For 64-bit operations, use BigInt (e.g., 0xABCD1234n & 0xFFFF0000n).

What is the difference between >> and >>> in JavaScript?

The >> operator (signed right shift) propagates the sign bit, preserving the sign of negative numbers. The >>> operator (unsigned right shift) fills from the left with zeros regardless of the sign. For example, -8 >> 2 = -2, but -8 >>> 2 = 1073741822 (because -8 becomes a large unsigned 32-bit number).

When should I use bitwise operators in JavaScript instead of Math functions?

Use bitwise operators when working with flags, binary protocols, color manipulation, or performance-critical integer math. For example, x | 0 is a fast floor for positive 32-bit numbers, and n & 1 checks parity faster than n % 2. However, they sacrifice readability — use them when the performance gain justifies it.

Related Tools

Bitwise Calculator →

AND, OR, XOR, NOT, shifts

Bitwise Guide →

Complete operations reference

Reference →

Quick lookup tables

Python Bitwise →

Infinite-precision ints

Try These Operations Live

Test JavaScript bitwise patterns directly in the browser with our interactive bitwise calculator. See binary, hex, and decimal results update in real time.