Python Bitwise Operators

Bitwise operators let you work directly on the individual bits of an integer. They are faster than arithmetic operations at the hardware level and show up everywhere from permission systems and network protocols to cryptography and game development. Understanding them makes you a more capable Python programmer and helps you read low-level code with confidence.

Python provides six bitwise operators: & (AND), | (OR), ^ (XOR), ~ (NOT), << (left shift), and >> (right shift). Each one operates on the binary representation of integers rather than their decimal values. That distinction is the key to understanding why they behave the way they do.

Binary Basics: What You Need to Know

Every integer in Python has an underlying binary representation — a sequence of 1s and 0s called bits. The number 5 in binary is 101, meaning one group of four, zero groups of two, and one group of one. The number 3 in binary is 011. Bitwise operators compare or manipulate those bit positions directly.

Python gives you a convenient way to see the binary form of any integer using the built-in bin() function. The prefix 0b in the output simply tells Python the literal is binary.

print(bin(5))   # 0b101
print(bin(3))   # 0b011
print(bin(12))  # 0b1100
print(bin(255)) # 0b11111111
Note

Python integers have arbitrary precision, meaning they are not limited to 8, 16, or 32 bits the way integers in C or Java are. This affects how the bitwise NOT operator behaves, as you will see below.

You can also write integer literals directly in binary by using the 0b prefix. Python treats 0b101 and 5 as the same value.

a = 0b101   # same as 5
b = 0b011   # same as 3
print(a)    # 5
print(b)    # 3

The Six Bitwise Operators

AND ( & )

The AND operator compares each bit position of two integers. The result bit is 1 only when both corresponding bits are 1. Otherwise the result bit is 0.

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

result = a & b
print(bin(result))  # 0b1000
print(result)       # 8

# Bit-by-bit view:
#   1010
# & 1100
# ------
#   1000

AND is commonly used to check whether a specific bit is set, or to clear (zero out) certain bits while leaving others untouched.

OR ( | )

The OR operator returns a 1 in each bit position where at least one of the two operands has a 1.

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

result = a | b
print(bin(result))  # 0b1110
print(result)       # 14

# Bit-by-bit view:
#   1010
# | 1100
# ------
#   1110

OR is typically used to set (turn on) specific bits in a value without disturbing the other bits.

XOR ( ^ )

XOR, short for exclusive OR, returns a 1 where the two bits are different and a 0 where they are the same.

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

result = a ^ b
print(bin(result))  # 0b0110
print(result)       # 6

# Bit-by-bit view:
#   1010
# ^ 1100
# ------
#   0110

XOR has a useful property: applying it twice with the same value returns the original number. This makes it the foundation of many simple encryption and data-toggling techniques.

x = 42
key = 0b10101010

encrypted = x ^ key
decrypted = encrypted ^ key

print(decrypted)  # 42 — back to the original

NOT ( ~ )

The NOT operator flips every bit in a number. Because Python integers are signed and have arbitrary precision, ~n is always equal to -(n + 1). This follows from two's complement representation.

a = 5
print(~a)   # -6

b = 0
print(~b)   # -1

c = -1
print(~c)   # 0
Watch Out

The NOT operator is one of the most surprising for beginners. ~5 is -6, not 2. If you are expecting a simple bit flip to a positive number, you are likely thinking of a fixed-width unsigned integer (like in C). Python does not work that way.

Left Shift ( << )

The left shift operator moves all bits in a number to the left by a specified number of positions, filling the vacated positions on the right with zeros. Shifting left by one position is mathematically equivalent to multiplying by 2.

a = 0b0001   # decimal 1

print(a << 1)   # 2   (0b0010)
print(a << 2)   # 4   (0b0100)
print(a << 3)   # 8   (0b1000)
print(a << 4)   # 16  (0b10000)

# Useful for powers of 2:
print(1 << 8)   # 256
print(1 << 10)  # 1024
Pro Tip

Left shifting by n is equivalent to multiplying by 2**n, but can be faster in performance-critical code because it maps directly to a hardware instruction. In most Python programs the difference is negligible, but it is worth knowing.

Right Shift ( >> )

The right shift operator moves all bits to the right by a given number of positions. Bits shifted off the right end are discarded. For positive integers, this is equivalent to floor-dividing by 2 for each position shifted.

a = 0b1000   # decimal 8

print(a >> 1)   # 4   (0b0100)
print(a >> 2)   # 2   (0b0010)
print(a >> 3)   # 1   (0b0001)
print(a >> 4)   # 0   (0b0000 — bits shifted out)

# Halving:
value = 200
print(value >> 1)   # 100
print(value >> 2)   # 50

For negative integers, Python preserves the sign bit (arithmetic right shift), so right-shifting a negative number keeps it negative.

print(-8 >> 1)   # -4
print(-8 >> 2)   # -2

Practical Uses in Real Code

Checking Whether a Bit Is Set

A classic use of AND is testing whether a particular bit position is active. This pattern is everywhere in systems programming, hardware communication, and permission flags.

# Check if bit at position n is set
def is_bit_set(value, n):
    mask = 1 << n
    return bool(value & mask)

flags = 0b10110101

print(is_bit_set(flags, 0))  # True  (bit 0 is 1)
print(is_bit_set(flags, 1))  # False (bit 1 is 0)
print(is_bit_set(flags, 2))  # True  (bit 2 is 1)
print(is_bit_set(flags, 7))  # True  (bit 7 is 1)

Setting, Clearing, and Toggling Bits

The three most common bit manipulation tasks are setting a bit to 1, clearing it to 0, and toggling it between the two. Each uses a different operator.

value = 0b0000

# Set bit 3 (OR with a mask)
value = value | (1 << 3)
print(bin(value))   # 0b1000

# Clear bit 3 (AND with inverted mask)
value = value & ~(1 << 3)
print(bin(value))   # 0b0

# Toggle bit 2 (XOR with a mask)
value = value ^ (1 << 2)
print(bin(value))   # 0b100

value = value ^ (1 << 2)
print(bin(value))   # 0b0  — toggled back off

Unix-Style Permission Flags

File permission systems like Unix use bit flags where each bit represents a different permission. Python's os module exposes these constants directly.

import stat

# Common permission flags
READ    = 0b100   # 4
WRITE   = 0b010   # 2
EXECUTE = 0b001   # 1

# Give a user read and write permission
permissions = READ | WRITE
print(bin(permissions))   # 0b110  (6 in decimal)

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

# Revoke write permission
permissions = permissions & ~WRITE
print(bin(permissions))   # 0b100  (read only)

Packing and Unpacking Data

Bitwise operators are useful any time you need to pack multiple small values into a single integer — for example, storing an RGB color as one number.

# Pack RGB values into a single 24-bit integer
def pack_rgb(r, g, b):
    return (r << 16) | (g << 8) | b

# Unpack the individual channel values
def unpack_rgb(color):
    r = (color >> 16) & 0xFF
    g = (color >> 8)  & 0xFF
    b =  color        & 0xFF
    return r, g, b

color = pack_rgb(255, 128, 0)
print(hex(color))          # 0xff8000
print(unpack_rgb(color))   # (255, 128, 0)

Fast Even/Odd Check

The least significant bit of any integer tells you whether it is odd or even. Checking that bit with AND is marginally faster than using the modulo operator and expresses intent clearly in low-level code.

def is_even(n):
    return (n & 1) == 0

def is_odd(n):
    return (n & 1) == 1

print(is_even(42))   # True
print(is_odd(7))     # True
print(is_even(-4))   # True

Operator Precedence and Pitfalls

Bitwise operators have lower precedence than arithmetic operators but higher precedence than comparison operators. The order from highest to lowest among the bitwise group is: ~, then << and >>, then &, then ^, then |.

This precedence order trips up many programmers. Consider this expression:

# Unexpected result without parentheses
result = 5 & 3 == 1
print(result)   # False — evaluated as 5 & (3 == 1) which is 5 & False = 0

# What you likely meant:
result = (5 & 3) == 1
print(result)   # True
Note

When mixing bitwise and comparison operators, always use parentheses to make your intent explicit. The precedence rules are non-obvious and parentheses cost nothing in terms of performance.

Bitwise operators also only work on integers in Python. Applying them to floats raises a TypeError. If you need to manipulate the binary representation of a float, you must first convert it using the struct module.

# This raises TypeError:
# result = 3.14 & 1

# Use struct if you genuinely need float bit manipulation:
import struct

bits = struct.unpack('I', struct.pack('f', 3.14))[0]
print(bin(bits))   # binary representation of 3.14 as a 32-bit float

Key Takeaways

  1. Six operators, two categories: AND, OR, XOR, and NOT operate on corresponding bit positions. Left shift and right shift move all bits in one direction by a given count.
  2. NOT always returns -(n + 1): Python integers are signed with arbitrary precision, so ~n is not a simple bit flip to a positive number — it follows two's complement rules.
  3. Shifts are powers of two: Left-shifting by n multiplies by 2**n; right-shifting by n floor-divides by 2**n.
  4. Use parentheses with comparisons: Bitwise operators have lower precedence than comparisons in some languages but higher in Python's ordering relative to logical operators — when in doubt, parenthesize.
  5. Practical applications are everywhere: Permission flags, network protocols, color packing, data compression, and cryptography all rely on bit manipulation techniques built from these six operators.

Bitwise operators are one of those topics where the initial concept feels abstract but clicks quickly once you see them applied to real problems. Work through the RGB packing example, build a simple permission system, or write a tiny XOR cipher — hands-on practice with concrete output is the fastest path to making these operators feel natural.

back to articles