PythonBitwise Operators

Bitwise Operators

Bitwise operators work directly on the binary representation of integers, bit by bit, rather than on the numbers as a whole. They show up less often than arithmetic operators in everyday scripts, but they are essential for working with flags, masks, low-level protocols, and performance-sensitive numeric tricks.

The operators

Operator

Name

Description

&

AND

Bit is 1 only if both operand bits are 1

|

OR

Bit is 1 if either operand bit is 1

^

XOR

Bit is 1 if exactly one operand bit is 1

~

NOT

Flips every bit (inverts 0 and 1)

<<

Left shift

Shifts bits left, filling with 0

>>

Right shift

Shifts bits right (sign-extending for negatives)

Binary representation, briefly

Every integer is stored as a sequence of bits. Python lets you inspect this with the built-in bin() function, and write binary literals directly with a 0b prefix.

Looking at bits

Python
a = 0b1100  # 12
b = 0b1010  # 10

print(bin(a))  # '0b1100'
print(bin(b))  # '0b1010'
print(bin(a & b))  # AND
print(bin(a | b))  # OR
print(bin(a ^ b))  # XOR
0b1100
0b1010
0b1000
0b1110
0b110
Negative numbers and ~
Python integers are conceptually stored using two's complement with infinite precision, so `~x` is always equal to `-x - 1`. This is a common gotcha: `~5` is not some large unsigned bit pattern — it is simply `-6`. If you need a fixed-width bitmask (e.g. simulating an 8-bit or 32-bit inversion), mask the result yourself, for example `~x & 0xFF` for an 8-bit value.

~ and two's complement

Python
x = 5
print(~x)          # -6  (because ~x == -x - 1)

# Simulating an 8-bit NOT by masking to 8 bits
byte_value = 0b00000101  # 5
inverted_8bit = ~byte_value & 0xFF
print(bin(inverted_8bit))  # 0b11111010
Practical use case: flags and masks

Bitwise operators are the standard way to pack several boolean flags into a single integer — classic examples include Unix file permission bits or feature flags in a config value. Each bit position represents one on/off setting.

Permission flags with bitmasks

Python
READ = 0b100    # 4
WRITE = 0b010   # 2
EXECUTE = 0b001 # 1

# Grant read and write, but not execute
permissions = READ | WRITE
print(bin(permissions))  # 0b110

# Check if WRITE is set
has_write = bool(permissions & WRITE)
print(has_write)  # True

# Revoke WRITE (clear that bit)
permissions &= ~WRITE
print(bin(permissions))  # 0b100

# Toggle EXECUTE on/off
permissions ^= EXECUTE
print(bin(permissions))  # 0b101
  • Use | to set (turn on) a flag.

  • Use & with the flag to check whether it is set.

  • Use &= ~flag to clear (turn off) a flag.

  • Use ^= to toggle a flag between on and off.

Practical use case: fast multiply/divide by powers of 2

Shifting bits left by n positions is equivalent to multiplying by 2**n, and shifting right by n positions is equivalent to integer division by 2**n. CPU-level shifts are cheap, so this pattern shows up in performance-sensitive code and in bit-manipulation puzzles.

Shifts as multiply/divide

Python
value = 5

print(value << 1)  # 10   (5 * 2)
print(value << 3)  # 40   (5 * 8)
print(value >> 1)  # 2    (5 // 2, rounds toward negative infinity)

negative = -20
print(negative >> 2)  # -5  (-20 // 4)
Readability trade-off
`x * 2` and `x // 2` are usually clearer than `x << 1` and `x >> 1` for ordinary application code — modern Python and CPU optimizers already handle the arithmetic form efficiently. Reach for shifts when you are explicitly modeling bits (flags, protocols, binary formats), not as a general-purpose speed trick.