XOR is addition with the carry thrown away. Add two bits, keep the last digit, forget the overflow, so 1 + 1 = 10 becomes 0. Every property people find surprising about XOR falls out of that one sentence. Nothing carries between columns, so column 7 never learns what happened in column 6, so the operation is its own inverse, so running a value through twice returns you to where you started. Mathematicians call it addition over GF(2). Bit twiddlers call it the difference operator, because a 1 in the result means the two inputs disagreed in that column.
Reading the bit compare panel
The ladder stacks A on B on the result, right aligned so bit 0 lands in the last column, grouped in fours to match hex digits. Set bits in the result carry colour because a set bit here means something specific: those are the positions where A and B differ. Two identical values produce a row of zeros, which is why an equality check in hardware is a XOR followed by a test for zero.
Three readouts under the ladder come free once the result exists:
- Hamming distance is the count of 1s in the result. Two byte values differing in three positions have a Hamming distance of 3, and error correcting codes are designed around keeping that number large between any two valid words.
- Parity is whether the count is odd or even. Fold every bit of the result together with XOR and a single bit drops out, the same bit a serial line appends to catch one flipped bit in transit.
- Equality is the zero test. A zero result is the only case where the two inputs match in every column at the chosen width.
Width matters more than it looks. Each value is masked to the selected width before the operation runs, so at 8 bits a decimal input of 300 wraps to 44. The panel flags a value it had to truncate instead of quietly clipping it. At 64 bits the arithmetic runs on BigInt, so high bits stay exact rather than collapsing into the 32 bit signed range JavaScript uses for its own operator.
XOR undoes itself, which is the whole trick
Take any value, XOR in a mask, then XOR the same mask back in. You are holding the original. The mask cancels because every bit met its own copy and 1 ⊕ 1 = 0. The round trip strip under the stats runs this on your own numbers, so the claim is not something you have to take on faith.
That single property does the work in a long list of tricks that otherwise look unrelated:
| Job | Expression | Why XOR and not something else |
|---|---|---|
| Toggle a flag | flags ^= MASK | OR only sets, AND only clears. XOR flips whatever was there, so one line covers both directions |
| Compare two values | (a ^ b) == 0 | The result names the differing bits, not merely the fact of a difference |
| Count differing bits | popcount(a ^ b) | Hamming distance in two instructions, used in image hashing and nearest neighbour search |
| Rebuild a lost block | d2 = d1 ^ d3 ^ p | Reversibility means any one missing operand comes back from the rest |
| Find the unpaired value | reduce(^, list) | Pairs annihilate, so duplicates cancel and the lone value survives |
| Gray code a counter | g = n ^ (n >> 1) | Adjacent codes end up one bit apart, which stops glitches on parallel bus lines |
| Encrypt a byte stream | c = m ^ keystream | Decryption is the identical operation, so one code path serves both directions |
Parity, RAID and why one dead drive is survivable
Line up four data blocks, XOR them into a fifth. Lose any single block out of the five and the survivors XOR back to the missing one. The parity block is not a copy of anything, and it costs one drive out of five rather than doubling the array, which is the arrangement RAID 5 sells.
The parity panel above runs this on real hex, so you watch the recovered block match its original byte for byte. Change one hex digit in a data block and the parity moves with it. Change a digit in the parity block instead and the recovery of a data block goes wrong in exactly that position, which is the failure mode behind silent corruption during a rebuild.
The limits deserve stating plainly. One parity block survives one loss. Two failed drives during a long rebuild and the array is gone, which is why RAID 6 carries a second parity computed over a different field rather than a second XOR. Parity also detects nothing on its own: it tells you a block is missing only when the drive says so. Silent bit rot passes straight through, since the array has no way to know which of the five blocks is lying. Filesystems built on checksums exist for that reason.
The same arithmetic scales down. A parity bit on a serial line is the XOR of eight data bits, catching any odd number of flips. It misses every even number, including the common case of two adjacent bits going down together in a burst, which is why real links use CRC instead. A CRC is still XOR underneath, applied as polynomial division over GF(2) with shifts feeding the register.
XOR ciphers: real inside stream ciphers, broken on their own
The Key XOR panel builds a repeating key cipher, sometimes called Vigenere over bytes. It earns its place as a teaching device and fails as security. Two facts explain both halves.
First, the one time pad is provably unbreakable and is nothing but XOR. Take a key stream as long as the message, truly random, never reused, and the ciphertext carries no information about the plaintext at all. Every stream cipher in service, ChaCha20 and AES in counter mode included, is an attempt to generate that key stream from a short key. The final step in each of them is a XOR.
Second, the moment a key repeats the guarantee is gone. Split the ciphertext into columns by key length and each column becomes a single byte XOR, which frequency analysis breaks against English text almost immediately. The key length itself falls out of repeated distances in the ciphertext or a Hamming distance search across candidate lengths. Reuse one key across two messages and an attacker XORs the ciphertexts together, cancelling the key entirely, leaving the two plaintexts XORed against each other and readable by hand with a little patience.
If you find XOR in production code
Treat it as obfuscation, never as encryption. Malware authors XOR strings with a single byte to dodge naive scanners, and plenty of desktop software has shipped configuration files hidden the same way. Both come apart in minutes. If a system needs confidentiality, reach for an authenticated cipher from a maintained library. If a system only needs to keep a value out of a plain text grep, XOR is honest enough as long as nobody claims more for it.
Language traps worth knowing before you trust an expression
- C and C++ precedence. The caret binds looser than the comparison operators, so a ^ b == c parses as a ^ (b == c). This has been a source of quiet bugs since 1972. Parenthesise every XOR sitting near a comparison.
- PostgreSQL. The caret is exponentiation, not XOR. Writing 5 ^ 3 returns 125. Bitwise XOR there is the # operator, and MySQL and T-SQL each spell it differently again.
- Python. The caret works on booleans as well as integers, and True ^ True is False. It also works on sets, where it means symmetric difference, the same idea one level up.
- JavaScript. The operator converts to 32 bit signed integers first, so anything above 2147483647 changes value before the XOR happens. Wider masks need BigInt. This page uses BigInt, which is why a 64 bit result here matches C rather than a browser console.
- Excel and spreadsheets. There is no bitwise XOR operator, and the XOR() function is logical, returning TRUE for an odd count of TRUE arguments. BITXOR() is the bitwise one and stops at 48 bits.
The swap trick, and why it belongs in an interview and nowhere else
Three lines swap two integers with no temporary variable, which is a neat demonstration of reversibility:
a ^= b b ^= a // b now holds the original aa ^= b // a now holds the original b
It fails the moment both names point at the same storage. Call it with one variable twice, or with two array indices that happen to be equal, and the first line zeroes the value. Both end up as 0 and the original is gone. Compilers have folded ordinary swaps into a single register exchange for decades, so the trick is slower as well as more fragile. Know it, recognise it in old code, then write the version with a temporary.
The array puzzles hold up better. Given a list where every value appears twice except one, XOR the whole list together: the pairs cancel and the odd one out is what remains, in one pass and constant memory. The same idea finds a missing number in a range by XORing the range against the list. Both are worth keeping because they show up in stream processing, where holding the data is not an option.
Inside the gate
XOR is the expensive one in the basic set. A CMOS NAND costs four transistors and a NOR costs the same. An XOR needs eight to twelve depending on the layout, since there is no way to build it as a single pull up and pull down network. Chip designers restructure logic to avoid long XOR chains for that reason, though the operation is unavoidable in one place: the sum bit of a full adder is a ⊕ b ⊕ carry_in, so every addition a processor performs runs through a row of them.
Linear feedback shift registers tap a few bit positions, XOR them together and feed the result back into the register. That builds pseudo random sequences, scramblers for serial links, and the CRC engines in every Ethernet frame. XOR sits at the centre of all three because of linearity over GF(2): the algebra stays predictable, which keeps the sequence analysable and the hardware small.
The map above hints at the other side of that linearity. XOR is not linearly separable, so a single layer perceptron cannot represent it. Minsky and Papert made the point in 1969, funding for neural network research dried up for a decade, and the eventual answer, a hidden layer, is what the field was rebuilt on. A two input logic gate is a strange place for that argument to have started.
Where this tool stops
The bit compare panel takes unsigned integers up to 64 bits. Negative decimals are rejected rather than converted, because a two's complement pattern depends on a width you have not stated yet, so run them through the number base converter first and paste the unsigned pattern back. Floating point values are rejected outright. There is no expression parser, so a mixed line such as (a ^ b) & ~c has to be worked one step at a time, and the bitwise calculator covers shifts and NOT. The key panel handles UTF-8 text and hex, not file uploads, and it slows down past a few hundred kilobytes. The parity panel is fixed at four data blocks of equal length, which models RAID 5 and not the double parity of RAID 6.
Everything here runs in your browser. No value, key or block is sent anywhere, which is the only sane arrangement for a page inviting you to paste a key into it.
