DevGizmo
Back to Blog
number·

Bitwise Operations in Programming: AND, OR, XOR, NOT, and Bit Shifts Explained

Bitwise operations manipulate individual bits within integers and are essential in systems programming, embedded development, and performance-critical code. Learn how each operation works with practical examples.

bitwisebinaryoperatorslow-level-programming

What Are Bitwise Operations?

Bitwise operations treat integer values as sequences of individual bits and apply logical operations to each bit position in parallel. They are computed directly by the CPU in a single instruction, making them extremely fast.

They are used in:

  • Flags and permissions (e.g., Unix file permissions: rwxr-xr-x = 0755)
  • Network programming (subnet masks, IP addresses)
  • Embedded systems (hardware register manipulation)
  • Cryptography and hashing algorithms
  • Performance optimisations (fast multiplication/division by powers of 2)

The Six Core Operations

AND (&)

Output bit is 1 only if both input bits are 1. Used to test or clear specific bits.

  0110 1100
& 0011 1111
= 0010 1100

Use case — test if a bit is set:

const HAS_WRITE = 0b00000010;
if (permissions & HAS_WRITE) {
  // write flag is set
}

OR (|)

Output bit is 1 if either input bit is 1. Used to set specific bits.

  0110 1100
| 0011 1111
= 0111 1111

Use case — set a flag:

const FLAG_BOLD = 0b0001;
const FLAG_ITALIC = 0b0010;

let style = 0;
style = style | FLAG_BOLD; // set bold
style = style | FLAG_ITALIC; // set italic

XOR (^)

Output bit is 1 if the input bits are different. Used to toggle bits and in many cryptographic primitives.

  0110 1100
^ 0011 1111
= 0101 0011

Use case — toggle a flag:

style = style ^ FLAG_BOLD; // toggle bold on/off

XOR swap trick (no temporary variable):

a = a ^ b;
b = a ^ b; // now b = original a
a = a ^ b; // now a = original b

NOT (~)

Inverts all bits. In JavaScript (32-bit signed integers), ~n = -(n + 1):

~5; // -6
~0; // -1
~-1; // 0

Use case — clear specific bits (AND with NOT of mask):

const FLAG_BOLD = 0b0001;
style = style & ~FLAG_BOLD; // clear bold flag

Left Shift (<<)

Shifts all bits left by N positions, filling vacated positions with 0. Equivalent to multiplying by 2ⁿ.

0000 0011 << 2 = 0000 1100  (3 × 4 = 12)
1 << 0; // 1   (2⁰)
1 << 1; // 2   (2¹)
1 << 4; // 16  (2⁴)
1 << 10; // 1024

Right Shift (>> and >>>)

Arithmetic right shift (>>): Preserves the sign bit (fills with 1 for negative numbers). Used for integers.

Logical right shift (>>>): Always fills with 0, regardless of sign. Treats the value as unsigned.

-8 >> 1; // -4  (arithmetic)
-8 >>> 1; // 2147483644  (logical, treats as unsigned 32-bit)

Compound Patterns

Check if a number is even or odd

n & (1 === 0); // even
n & (1 === 1); // odd

Check if a number is a power of 2

n > 0 && (n & (n - 1)) === 0;

Get the lowest set bit

n & -n;

Count set bits (population count / popcount)

function popcount(n) {
  let count = 0;
  n = n >>> 0; // treat as unsigned 32-bit
  while (n) {
    count += n & 1;
    n >>>= 1;
  }
  return count;
}

JavaScript Gotcha: 32-Bit Integers

JavaScript numbers are 64-bit floats, but bitwise operators convert operands to signed 32-bit integers before operating. This means:

  • No bitwise operation on a JavaScript number can produce a result outside the 32-bit signed range (−2,147,483,648 to 2,147,483,647)
  • Large integers like Number.MAX_SAFE_INTEGER get truncated

For 64-bit bitwise operations, use BigInt:

const a = 0xffffffffffffffffn; // BigInt
const b = a & 0x00000000ffffffffn;

Bitwise Flags in Real-World Code

Unix file permissions use a 12-bit field. The command chmod 755 sets:

  • Owner: rwx = 0b111 = 7
  • Group: r-x = 0b101 = 5
  • Others: r-x = 0b101 = 5

Combined: 0b111101101 = 0755 (octal).

The stat() syscall returns these permissions as an integer, and libraries compare them with bitwise AND to check specific permissions.

Try it yourself

Put these concepts into practice with the free online tool on DevGizmo.