The Quest Begins (The "Why")
I still remember the first time I faced an interview question that asked me to add two integers without using the + or - operators. At first I thought, “Sure, I’ll just loop and increment,” but the moment I realized I couldn’t use any arithmetic at all, my brain hit a wall. I started sketching out truth tables, trying to mimic the way a calculator works, and ended up with a tangled mess of conditionals that felt like trying to solve a Rubik’s Cube blindfolded.
The frustration was real. I knew there had to be a cleaner way—something that leveraged the very representation of numbers inside the machine. That’s when I dove back into the basics of binary arithmetic and discovered a trick that felt like uncovering a hidden cheat code.
The Revelation (The Insight)
The secret lies in how binary addition actually works. When you add two bits, you have two outputs:
-
Sum bit – the XOR of the two bits (
a ^ b). -
Carry bit – the AND of the two bits, shifted left one position (
(a & b) << 1).
If you add the sum and the carry together, you get the correct result. But here’s the kicker: the carry itself may generate a new carry when added to the sum. So you repeat the process: compute a new sum from the previous sum and carry, compute a new carry, and keep going until there’s no carry left.
Why does this terminate? Each iteration pushes the carry bits further left. In a fixed‑width integer, after at most word size‑of‑int shifts the carry must fall off the left end, becoming zero. In other words, the algorithm finishes in **O(number of bits)* steps, which for 32‑ or 64‑bit integers is effectively constant time.
It’s not magic; it’s just the grade‑school addition algorithm expressed in binary, where XOR handles addition without carry and AND+shift handles the carry generation.
Wielding the Power (Code & Examples)
The “before” – a naïve, painful attempt
def add_without_plus(a, b):
# Try to simulate addition by repeated increment/decrement
# (this is O(|b|) and fails for negative numbers)
if b >= 0:
for _ in range(b):
a += 1 # we cheat by using + here!
else:
for _ in range(-b):
a -= 1
return a
Yeah, I cheated by using + inside the loop, and the runtime blows up when b is large. Not interview‑friendly at all.
The “after” – the bitwise spell
def get_sum(a: int, b: int) -> int:
"""Return a + b using only bitwise operators."""
MASK = 0xFFFFFFFF # pretend we are working with 32‑bit unsigned ints
MAX_INT = 0x7FFFFFFF
while b != 0:
# carry now holds the bits that will be added to the next higher position
carry = (a & b) & MASK
# sum without carry
a = (a ^ b) & MASK
# shift carry left and repeat
b = (carry << 1) & MASK
# If a is negative in two's complement form, convert back to Python's signed int
return a if a <= MAX_INT else ~(a ^ MAX_INT)
Why it works:
-
a ^ bgives the bitwise sum where no position has a carry (just like adding 0+0, 0+1, 1+0). -
a & bfinds every position where both bits are 1 – those are the exact spots that generate a carry. Shifting left moves the carry to its proper higher‑order position. - The loop repeats until there’s no carry left, meaning all positions have been resolved.
The masking (& MASK) is crucial in Python because ints are of unlimited precision; we artificially constrain ourselves to 32‑bit two’s‑complement arithmetic to avoid an infinite loop caused by sign extension.
Common traps
- Forgetting the mask – Without limiting to a fixed width, the carry can keep propagating forever in Python’s infinite‑size ints, leading to an endless loop.
-
Assuming the result is already signed – After the loop,
aholds an unsigned 32‑bit pattern. If the true sum is negative, we must convert it back using two’s‑complement logic (~(a ^ MAX_INT)).
Interview‑style problems
Problem 1 (LeetCode 371 – Sum of Two Integers)
Given two integers
aandb, return the sum ofaandbwithout using the operators+and-.
Solution: The get_sum function above solves it in O(1) time and O(1) space.
Problem 2 (Custom – Find missing number in 0…n)
You have an array containing
ndistinct numbers taken from0, 1, 2, ..., n. Find the missing one.
Solution: XOR all indices and all values; the duplicate pairs cancel, leaving the missing number. This uses the same principle that x ^ x = 0 and x ^ 0 = x, which is another beautiful bit‑wise identity, but the core idea—cancelling pairs via XOR—mirrors the carry‑propagation reasoning we just saw.
Why This New Power Matters
Mastering this trick does more than let you ace a brain‑teaser interview. It gives you a deeper intuition for how computers actually perform arithmetic, which shows up in:
- Low‑level systems work – writing emulators, cryptographic primitives, or performance‑critical loops where you want to avoid expensive arithmetic.
- Bitmask DP – many DP transitions rely on quickly toggling bits; understanding carry propagation helps you reason about complex state transitions.
- Problem‑solving mindset – you start seeing numbers not as abstract values but as patterns of bits that can be manipulated with simple logical ops.
When you can add two numbers with just XOR, AND, and shift, you’ve leveled up from “I know how to call a library function” to “I can speak the machine’s native language.”
Your Next Challenge
Try implementing subtraction (a - b) using only bitwise operators (hint: use two’s complement: a - b == a + (~b + 1)). Test it with negative inputs and verify the results match Python’s native -.
If you crack that, you’ll have a full‑blown integer ALU in a few lines of code—proof that a handful of bitwise tricks can replace entire arithmetic units.
Now go forth, wield your XOR like a lightsaber, and may your carries always resolve quickly! 🚀
Top comments (0)