Topics › Bit Manipulation › Interview Problems
Sum of Two Integers
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
Given two integers a and b, return a + b without using the + or - operators. Bitwise operators (&, |, ^, ~), shifts and comparisons are allowed. The exercise is really “rebuild addition out of the logic gates that a CPU’s adder is made of”.
Constraints: -1000 ≤ a, b ≤ 1000 on LeetCode. The methods below work for every pair of 32-bit int values and, exactly like Java’s own +, wrap around on overflow: Integer.MAX_VALUE plus 1 gives Integer.MIN_VALUE.
Example 1
a = 1, b = 2
answer = 3 // 01 + 10 = 11, no column produces a carry
Example 2
a = -1, b = 1
answer = 0 // 111...111 + 000...001: a carry ripples through all 32 bits and falls off the top
Background: what addition looks like in binary
Add two binary numbers the way you add decimal ones on paper: column by column from the right, carrying into the next column. In base 2 a column has three inputs, the bit x of a, the bit y of b, and the carry c coming in from the right, and two outputs:
- the sum bit is
1when an odd number of the three inputs are1, which isx ^ y ^ c; - the carry out is
1when at least two inputs are1, which is(x & y) | (c & (x ^ y)).
Ignore the incoming carry for a moment and look at the two-input case: 0+0 = 0, 0+1 = 1, 1+1 = 10. The sum bit is x ^ y and the carry is x & y. XOR is “addition without carrying” and AND is “where does a carry happen”. Both approaches below are built on that one observation.
Negative numbers need no special case. Java stores int in two’s complement, and the whole reason hardware uses two’s complement is that the same unsigned adder produces correct signed results: add the bit patterns, throw away any carry out of bit 31, and the remaining 32 bits are the right answer. -1 is all ones; adding 1 turns every bit to 0 and pushes a carry out of the top, which is discarded, leaving 0.
Approach 1: Brute force, a bit-by-bit ripple-carry adder
Intuition. Simulate the paper method literally. Walk from bit 0 to bit 31, read one bit of each operand, produce the sum bit with the full-adder formula, and pass the carry to the next position. A loop counter i++ would itself use +, so instead we walk a one-bit mask leftwards with mask <<= 1. After bit 31 the 1 is shifted out, the mask becomes 0, and the loop ends after exactly 32 rounds.
Algorithm.
- Start with
result = 0andcarry = 0. - For each
maskin1, 2, 4, ...until it becomes0:- read
xandy, the bits ofaandbat that position; - if
x ^ y ^ carryis1, set that bit inresult; - update
carry = (x & y) | (carry & (x ^ y)).
- read
- Return
result. The carry left after bit 31 is dropped, which is exactly 32-bit wrap-around.
static int rippleCarry(int a, int b) {
int result = 0, carry = 0;
for (int mask = 1; mask != 0; mask <<= 1) { // 32 rounds: after bit 31 the 1 falls off the top
int x = (a & mask) != 0 ? 1 : 0;
int y = (b & mask) != 0 ? 1 : 0;
if ((x ^ y ^ carry) == 1) result |= mask; // sum bit of a full adder
carry = (x & y) | (carry & (x ^ y)); // carry into the next position
}
return result;
}
Complexity. Time O(32), which is O(1): one round per bit position whatever the inputs. Space O(1).
It always pays for all 32 columns, even for 1 + 2, and each round does several operations. It is the “obviously correct” baseline that the next approach compresses.
Approach 2: Optimal, XOR for the sum and AND-shift for the carries
Intuition. Do every column at once. a ^ b is the sum of all columns with carries ignored. a & b marks the columns where both bits are 1, which are exactly the columns that generate a carry, and a carry lands one column to the left, so the carries form the number (a & b) << 1. That gives the identity
a + b == (a ^ b) + ((a & b) << 1)
We have replaced one addition with another, so it looks circular. The progress is in the second operand: if b has t trailing zeros, then a & b has at least t (AND can only clear bits) and the shifted carry has at least t + 1. Each round pushes the carry at least one position left, so after at most 32 rounds the carry has shifted out of the int and becomes 0. At that point the first operand is the answer.
Algorithm.
- While
b != 0:carry = (a & b) << 1;a = a ^ b;b = carry.
- Return
a.
static int optimal(int a, int b) {
while (b != 0) {
int carry = (a & b) << 1; // where both bits are 1, a carry moves one position left
a = a ^ b; // add every position, ignoring carries
b = carry; // the carries still have to be added
}
return a;
}
Dry run on a = 5, b = 3, showing the low 4 bits:
| round | a | b | a ^ b | (a & b) « 1 |
|---|---|---|---|---|
| 1 | 0101 | 0011 | 0110 | 0010 |
| 2 | 0110 | 0010 | 0100 | 0100 |
| 3 | 0100 | 0100 | 0000 | 1000 |
| 4 | 0000 | 1000 | 1000 | 0000 |
b is now 0 and a = 1000, which is 8. Each round is one more step of the same carry chain, so the number of rounds is one more than the longest run of carries. -1 + 1 is the worst case: the carry walks through all 32 positions before it leaves the word.
Complexity. Time O(32) in the worst case and usually far fewer rounds. Space O(1).
Approach 3: The same identity, written recursively
Intuition. The identity a + b == (a ^ b) + ((a & b) << 1) reads naturally as a recursive definition, with b == 0 as the base case because adding zero changes nothing. The termination argument is the same as before: the second argument gains a trailing zero on every call.
static int recursive(int a, int b) {
if (b == 0) return a;
return recursive(a ^ b, (a & b) << 1);
}
Complexity. Time O(32). Space O(32) for the call stack, which is why the loop version is preferred, although 32 frames is harmless.
Follow-up: subtraction
In two’s complement, -b == ~b + 1: flipping every bit of b gives -b - 1. So a - b is two of our additions and no - operator:
static int subtract(int a, int b) {
return optimal(a, optimal(~b, 1));
}
Edge cases and pitfalls
- Overflow. Java’s
intarithmetic wraps, and so do these methods: bits shifted past position 31 disappear.Integer.MAX_VALUEplus1returnsInteger.MIN_VALUEandInteger.MIN_VALUEplus itself returns0, the same results+would give. The tests compare againsta + bover a grid of values and 2000 random pairs. - Negative numbers. Nothing special is needed in Java. In a language with unbounded integers (Python), a negative carry never falls off the top, so the loop would run forever unless you mask every step to 32 bits and convert the result back to a signed value.
- Only left shifts are used. There is no
>>versus>>>question here. The left shift is what makes the loop terminate. - Zero operands.
b == 0returns immediately;a == 0finishes after one round becausea & bis0. - “Is
i++allowed?” Strictly it is addition. The mask loop in Approach 1 sidesteps the question, and Approaches 2 and 3 need no counter at all.
Related problems
- Introduction to Bits and Tricks covers two’s complement and the bitwise operators used here.
- Swap Two Numbers relies on XOR behaving like addition without carries.
- Divide two numbers without multiplication and division rebuilds another arithmetic operator from shifts.
- Number of 1 Bits and Reverse Bits are the neighbouring Blind 75 bit problems.