DEV Community

Cover image for Handy Bit Manipulation Tricks
Manuj Sankrit
Manuj Sankrit

Posted on

Handy Bit Manipulation Tricks

I was digging through React's source code and stumbled upon some bitmask logic for handling component states. At first glance, it looked cryptic—just a bunch of &, |, and << operators. But once I understood what was happening, it clicked. These operations are ridiculously fast and efficient.

Here are some tricks I find myself reaching for when performance actually matters.

Basic Bit Operations

Setting a Bit

To set the bit at position pos, use the bitwise OR operator. Think of (1 << pos) as a laser pointer—you're pointing it exactly at one spot, and | is the "turn on" switch:

function setBit(n: number, pos: number): number {
  return n | (1 << pos);
}
Enter fullscreen mode Exit fullscreen mode

Example: Setting bit at position 2 in 0b1010 gives us 0b1110

   0b1010
 | 0b0100  (laser pointer at position 2)
-----------
   0b1110
Enter fullscreen mode Exit fullscreen mode

Clearing a Bit

To clear a bit at position pos, we AND with the inverted mask:

function clearBit(n: number, pos: number): number {
  return n & ~(1 << pos);
}
Enter fullscreen mode Exit fullscreen mode

Example: Clearing bit at position 1 in 0b1110 gives 0b1100

   0b1110
 & 0b1101
-----------
   0b1100
Enter fullscreen mode Exit fullscreen mode

Toggling a Bit

Toggling is straightforward with XOR:

function toggleBit(n: number, pos: number): number {
  return n ^ (1 << pos);
}
Enter fullscreen mode Exit fullscreen mode

Example: Toggling bit at position 2 in 0b10100b1110

   0b1010
 ^ 0b0100
-----------
   0b1110
Enter fullscreen mode Exit fullscreen mode

Checking a Bit

To check if a bit is set:

function checkBit(n: number, pos: number): boolean {
  return ((n >> pos) & 1) !== 0;
}
Enter fullscreen mode Exit fullscreen mode

Example: Checking bit at position 1 in 0b0110

   0b0110
 >>     1
-----------
   0b0011
 & 0b0001
-----------
   0b0001 (true)
Enter fullscreen mode Exit fullscreen mode

⚠️ JS/TS Gotcha: Bitwise operators in JavaScript convert numbers into 32-bit signed integers. If you're working with large numbers (above 2³¹-1), like database IDs or timestamps, this magic will turn into a nightmare. For those cases, stick with BigInt or regular arithmetic.

Some Clever Tricks

Check if a Number is a Power of 2

This one's pretty neat:

function isPowerOfTwo(n: number): boolean {
  return n > 0 && (n & (n - 1)) === 0;
}
Enter fullscreen mode Exit fullscreen mode

The trick here is that powers of 2 have exactly one bit set(means one bit is 1). When you subtract 1, all the bits after that flip, so the AND operation gives you 0.

n = 8 (0b1000)
n - 1 = 7 (0b0111)
n & (n - 1) = 0b0000
Enter fullscreen mode Exit fullscreen mode

Count Set Bits (Brian Kernighan's Algorithm)

function countSetBits(n: number): number {
  let count = 0;
  while (n) {
    n = n & (n - 1);
    count++;
  }
  return count;
}
Enter fullscreen mode Exit fullscreen mode

This keeps clearing the rightmost set bit until nothing's left. Each iteration bumps the count.

0b1011 → 0b1010 → 0b1000 → 0b0000
count: 1      2        3
Enter fullscreen mode Exit fullscreen mode

Find the Rightmost Set Bit

function rightmostSetBit(n: number): number {
  return n & -n;
}
Enter fullscreen mode Exit fullscreen mode

This works because -n (two's complement) flips all the bits and adds 1, which isolates just the rightmost set bit.

n = 0b1010
-n = 0b0110
n & -n = 0b0010
Enter fullscreen mode Exit fullscreen mode

Swap Two Numbers Without a Temp Variable

function swap(a: number, b: number): [number, number] {
  a = a ^ b;
  b = a ^ b;
  a = a ^ b;
  return [a, b];
}
Enter fullscreen mode Exit fullscreen mode

XOR has this cool property where a ^ a = 0 and a ^ 0 = a, so you can swap without needing extra space.

Check if a Number is Even

function isEven(n: number): boolean {
  return (n & 1) === 0;
}
Enter fullscreen mode Exit fullscreen mode

Way faster than the modulo operator. The least significant bit is 0 for even numbers.

The Trade-off: Efficiency vs Readability

Now, here's the thing. Every concept has its pros and cons, and bit manipulation is no exception. Sure, these operations are super efficient and lean, but they definitely compromise the KISS principle.

If you go overboard with bit tricks in your codebase, you're gonna have a problem. Junior developers—well, the ones that haven't been replaced by AI yet 😁—might struggle to understand what's going on. Heck, even you might come back to your own code six months later and wonder what you were thinking.

So use bit manipulation wisely:

  • Performance-critical sections: Where every cycle counts
  • Well-documented code: Add comments explaining the bit magic
  • Specific algorithms: Like the ones mentioned above
  • Standard patterns: Stick to common tricks that developers recognize

But for everyday logic? Just write clear, readable code. n % 2 === 0 is perfectly fine for checking even numbers in most cases. Don't sacrifice code clarity just to show off your bit manipulation skills.

Where This Actually Matters

You'll see bit manipulation used in:

  • Permissions systems: Like Unix file permissions (chmod 755)
  • Feature flags: When you need to pack multiple boolean flags into a single integer
  • Subset generation: Iterating through all possible combinations
  • Performance-critical code: These operations literally execute in one CPU cycle

Quick Reference

Operation Code Example
Set bit n | (1 << pos) 0b1010 | (1 << 2)0b1110
Clear bit n & ~(1 << pos) 0b1110 & ~(1 << 1)0b1100
Toggle bit n ^ (1 << pos) 0b1010 ^ (1 << 2)0b1110
Check bit (n >> pos) & 1 (0b1010 >> 1) & 11 (set)
Power of 2 n & (n - 1) === 0 8 & 7 === 0true
Even/Odd n & 1 === 0 4 & 1 === 0true (even)
Rightmost set n & -n 0b1010 & -0b10100b0010

These patterns might look weird at first, but after using them a few times, they start to make sense. Just remember: with great power comes great responsibility. Use them where they make sense, not everywhere.

Pranipat 🙏!

Top comments (0)