Bitwise OR and XOR Explained

Setting bits, toggling flags, and simple encryption — two operators with very different personalities

OR and XOR look similar at first glance. Both take two inputs and produce one output, bit by bit. Both have truth tables with three 1s. But they solve fundamentally different problems. OR builds things up — it combines flags, merges bit patterns, and turns bits on without touching the rest. XOR spots differences — it toggles, encrypts, and has a reversibility property that makes it the most mathematically interesting bitwise operator. Here is when to use each one, with working code you can run.

OR: The "Set Bits" Operator

OR (| in C, Python, JavaScript, and most languages) returns 1 when at least one input bit is 1. Only 0 | 0 produces 0.

Bit ABit BA | B
000
011
101
111

The key insight: OR never turns a 1 into a 0. If a bit is already 1, OR'ing anything into that position leaves it as 1. This makes OR perfect for combining flags. You can OR a dozen permission bits together and no single flag accidentally clears another. Experiment with our bitwise OR calculator to see how bits accumulate.

OR Use Case: Unix File Permissions

Every file on a Linux or macOS system stores permissions as a bitfield. Read = 4 (0b100), Write = 2 (0b010), Execute = 1 (0b001). To grant read and write: 4 | 2 = 6 (0b110). To grant read, write, and execute: 4 | 2 | 1 = 7 (0b111).

# Chaining OR to combine file permissions
R = 0b100  # 4: read
W = 0b010  # 2: write
X = 0b001  # 1: execute

owner = R | W | X   # 0b111 = 7 (rwx)
group = R | X       # 0b101 = 5 (r-x)
other = R           # 0b100 = 4 (r--)

# The chmod 754 you've typed a thousand times
# is just three OR'd bitfields: 7|5|4
print(f"chmod {owner}{group}{other} myfile")  # chmod 754 myfile

This is why chmod 644 means owner can read+write, group and others can only read. The numbers 6 (4|2) and 4 are literally OR'd permission bits. Each digit is independently OR'd — permissions don't interfere across user categories.

XOR: The "Spot the Difference" Operator

XOR (^) returns 1 when the two input bits differ. Same bits produce 0, different bits produce 1. This is exclusive OR — exactly one input must be 1, not both.

Bit ABit BA ^ B
000
011
101
110

XOR has a property no other bitwise operator has: it is its own inverse. (A ^ B) ^ B = A. Apply XOR twice with the same key and you get back the original value. This is the foundation of XOR ciphers, the swap trick, and parity-based error detection. Try values on our bitwise XOR calculator to see the self-inverse property in action.

XOR Use Case 1: Toggling Bits

XOR with a 1 in position N flips that bit. XOR with 0 leaves it unchanged. This is how you toggle LEDs, flip boolean flags in a register, or invert specific bits in a control word — all without an if-statement.

# Toggle bit 2 on and off repeatedly
state = 0b0000   # Start with all zeros
state ^= (1 << 2)  # → 0b0100 (bit 2 turns ON)
state ^= (1 << 2)  # → 0b0000 (bit 2 turns OFF)
state ^= (1 << 2)  # → 0b0100 (back ON)

# This is how embedded systems toggle GPIO pins:
# PORTA ^= (1 << LED_PIN);  // one instruction, no branch

XOR Use Case 2: The Swap Trick

The classic interview favorite — swap two integers without a temporary variable using three XORs. It works because XOR is commutative and self-inverse.

a = 10  # 0b1010
b = 6   # 0b0110

a ^= b  # a = 0b1100 (12), b = 0b0110 (6)
b ^= a  # b = 0b1010 (10), a = 0b1100 (12)
a ^= b  # a = 0b0110 (6),  b = 0b1010 (10)
# a and b are now swapped

# In one line: a ^= b ^= a ^= b
# (but don't do this in production — it's unreadable)

Warning: this trick fails if a and b are the same variable (they both become 0). Modern compilers optimize a temp-variable swap to the same assembly anyway, so use this for understanding, not performance.

XOR Use Case 3: Simple XOR Cipher

XOR encryption is the simplest reversible cipher. Take your plaintext, XOR each byte with a key byte, and the output looks like garbage. XOR the ciphertext with the same key and the plaintext reappears. This is a stream cipher — the basis of one-time pads and the keystream mixing step in AES and ChaCha20.

def xor_cipher(data: bytes, key: bytes) -> bytes:
    """Encrypt or decrypt — same function for both directions."""
    return bytes(d ^ key[i % len(key)] for i, d in enumerate(data))

message = b"HELLO"
key = b"X"  # single-byte key for simplicity

encrypted = xor_cipher(message, key)
print(encrypted)  # b'\x11\x0c\x0c\x0c\x17' (garbage)

decrypted = xor_cipher(encrypted, key)
print(decrypted)  # b'HELLO' (original message restored)

# Real ciphers use per-message random keys and longer keystreams,
# but the core XOR operation is the same.

XOR Use Case 4: Parity and Checksums

XOR all bytes in a message together and you get a simple checksum. This is how RAID 5 rebuilds lost data: if you have blocks A, B, C and parity P = A ^ B ^ C, you can reconstruct any single lost block. Lost block B? A ^ C ^ P = B. XOR is also used in CRC calculations and the IP header checksum, though modern networks use more sophisticated algorithms.

OR vs XOR: When to Use Which

ScenarioUse ORUse XOR
Combining flagsYes — idempotent, safeNo — double-set cancels out
Toggling a bitNo — can't turn offYes — flips every time
EncryptionNo — not reversibleYes — self-inverse
Setting a bit to 1Yes — x | (1<<n)No — toggles if already 1
Parity / checksumNo — saturates to 1Yes — uniform distribution

Remember: OR accumulates — apply it twice with the same mask and nothing changes (idempotent). XOR toggles — apply it twice and you're back where you started (self-inverse). That single mathematical difference explains every use case in this article. Practice these patterns on our bitwise calculator or dive deeper with the complete bitwise operations guide.