371. Sum of Two Integers
Problem Description
You need to find the sum of two integers a
and b
without using the addition (+
) or subtraction (-
) operators.
The solution uses bit manipulation to simulate addition. Here's how it works:
-
Masking with
0xFFFFFFFF
: This ensures we're working with 32-bit integers, which is necessary for handling negative numbers in Python (since Python integers have arbitrary precision). -
The addition algorithm using bits:
a ^ b
gives the sum without considering carries (XOR adds bits where they differ)(a & b) << 1
calculates the carry bits (AND finds where both bits are 1, then shift left to add in the next position)- The process repeats until there's no carry left (
b
becomes 0)
-
Handling negative numbers:
- The final check
a < 0x80000000
determines if the result should be positive - If the result is negative (most significant bit is 1 in 32-bit representation), we need to convert it back using
~(a ^ 0xFFFFFFFF)
to get the correct negative value in Python
- The final check
Example walkthrough for a = 2, b = 3
:
- Binary:
a = 010
,b = 011
- First iteration:
- XOR:
010 ^ 011 = 001
- Carry:
(010 & 011) << 1 = 010 << 1 = 100
- XOR:
- Second iteration:
- XOR:
001 ^ 100 = 101
(which is 5) - Carry:
(001 & 100) << 1 = 000
- XOR:
- Since carry is 0, result is 5
The algorithm essentially mimics how we add numbers by hand in binary - add the digits, then handle the carries.
Intuition
When we can't use the +
or -
operators, we need to think about what addition actually means at the binary level. If you recall how you learned addition in elementary school, you add digits column by column and handle carries. Binary addition works the same way!
Let's think about what happens when we add two bits:
0 + 0 = 0
(no carry)0 + 1 = 1
(no carry)1 + 0 = 1
(no carry)1 + 1 = 10
(result is 0, carry is 1)
Looking at these patterns, we can make two key observations:
-
The sum without carry follows the XOR pattern:
a ^ b
gives us the result bit when we ignore carries0 ^ 0 = 0
,0 ^ 1 = 1
,1 ^ 0 = 1
,1 ^ 1 = 0
-
The carry happens only when both bits are 1, which is exactly what AND gives us:
a & b
- Since a carry affects the next higher bit position, we need to shift it left:
(a & b) << 1
- Since a carry affects the next higher bit position, we need to shift it left:
This leads us to a natural approach:
- First, calculate the sum ignoring carries using XOR
- Then, calculate what the carries would be
- Add the carries to our sum (but wait, we can't use
+
!) - So we repeat the process: treat the carries as a new number to "add" using the same XOR and AND technique
- Keep going until there are no more carries
The beauty of this approach is that we're essentially simulating the addition circuit that computers use at the hardware level. The XOR gate gives us the sum bit, and the AND gate (followed by a shift) gives us the carry bit. By iterating this process, we perform addition using only bitwise operations!
The complexity with Python comes from its unlimited integer precision, so we need to mask with 0xFFFFFFFF
to simulate 32-bit arithmetic and handle negative numbers specially at the end.
Solution Approach
The implementation uses bit manipulation to simulate binary addition without using arithmetic operators. Let's walk through the solution step by step:
Step 1: Handle 32-bit representation
a, b = a & 0xFFFFFFFF, b & 0xFFFFFFFF
Since Python has unlimited integer precision, we mask both numbers with 0xFFFFFFFF
to ensure we're working with 32-bit integers. This is crucial for handling negative numbers correctly.
Step 2: Iterative addition using XOR and AND
while b: carry = ((a & b) << 1) & 0xFFFFFFFF a, b = a ^ b, carry
This is the core addition loop:
a & b
: Finds all bit positions where botha
andb
have 1s (these positions will generate a carry)<< 1
: Shifts the carry bits left by one position (carries affect the next higher bit)& 0xFFFFFFFF
: Ensures the carry stays within 32 bitsa ^ b
: Calculates the sum without considering carries (XOR gives 1 when bits differ)- The loop continues until
b
(which holds the carry) becomes 0
Each iteration essentially says: "Add a
and b
ignoring carries, then prepare the carries for the next round."
Step 3: Handle negative numbers in Python
return a if a < 0x80000000 else ~(a ^ 0xFFFFFFFF)
0x80000000
is10000000000000000000000000000000
in binary (the sign bit position for 32-bit integers)- If
a < 0x80000000
, the result is positive and can be returned directly - If
a >= 0x80000000
, the most significant bit is 1, indicating a negative number in two's complement ~(a ^ 0xFFFFFFFF)
converts the 32-bit masked result back to Python's negative number representation
Example execution for a = 1, b = -1
:
- After masking:
a = 0x00000001
,b = 0xFFFFFFFF
- Iteration 1:
- Carry =
(0x00000001 & 0xFFFFFFFF) << 1 = 0x00000002
a = 0x00000001 ^ 0xFFFFFFFF = 0xFFFFFFFE
b = 0x00000002
- Carry =
- Multiple iterations later, when carries propagate through...
- Final result:
0
(as expected for1 + (-1)
)
The algorithm effectively mimics a hardware adder circuit, making it both elegant and efficient with O(1) space complexity and O(32) time complexity (at most 32 iterations for 32-bit integers).
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through adding a = 5
and b = 7
to get 12
.
Initial Setup:
a = 5
→ Binary:0101
b = 7
→ Binary:0111
Iteration 1:
- Calculate sum without carry:
a ^ b = 0101 ^ 0111 = 0010
(decimal 2) - Calculate carry:
a & b = 0101 & 0111 = 0101
(where both have 1s) - Shift carry left:
0101 << 1 = 1010
(decimal 10) - New values:
a = 0010
,b = 1010
Iteration 2:
- Sum without carry:
a ^ b = 0010 ^ 1010 = 1000
(decimal 8) - Calculate carry:
a & b = 0010 & 1010 = 0010
- Shift carry left:
0010 << 1 = 0100
(decimal 4) - New values:
a = 1000
,b = 0100
Iteration 3:
- Sum without carry:
a ^ b = 1000 ^ 0100 = 1100
(decimal 12) - Calculate carry:
a & b = 1000 & 0100 = 0000
(no common 1s) - Shift carry left:
0000 << 1 = 0000
(decimal 0) - New values:
a = 1100
,b = 0000
Result:
Since b = 0
(no more carries), we stop. The answer is a = 1100
(decimal 12).
Notice how in each iteration:
- XOR gives us partial sums (2 → 8 → 12)
- AND identifies where carries occur
- The carries get "added" in the next iteration
- The process naturally terminates when all carries are resolved
This mirrors exactly how you'd add these numbers by hand in binary:
0101 (5)
+ 0111 (7)
------
1100 (12)
Where you'd carry 1s from right to left until done!
Solution Implementation
1class Solution:
2 def getSum(self, a: int, b: int) -> int:
3 # Mask to get 32-bit representation
4 MASK = 0xFFFFFFFF
5 # Maximum value for a positive 32-bit signed integer
6 MAX_INT = 0x7FFFFFFF
7
8 # Convert both numbers to 32-bit unsigned representation
9 a = a & MASK
10 b = b & MASK
11
12 # Keep adding until there's no carry left
13 while b != 0:
14 # Calculate carry bits (AND operation finds where both bits are 1,
15 # left shift by 1 to move carry to next position)
16 carry = ((a & b) << 1) & MASK
17
18 # XOR gives sum without considering carry
19 a = a ^ b
20
21 # Update b to be the carry for next iteration
22 b = carry
23
24 # If the result is negative in 32-bit representation,
25 # convert back to Python's integer representation
26 # Check if MSB (bit 31) is set, indicating negative number
27 if a > MAX_INT:
28 # Convert from 32-bit two's complement to Python negative integer
29 return ~(a ^ MASK)
30 else:
31 # Positive number, return as is
32 return a
33
1class Solution {
2 /**
3 * Calculates the sum of two integers without using the + or - operators.
4 * Uses bit manipulation to perform addition.
5 *
6 * @param a the first integer
7 * @param b the second integer
8 * @return the sum of a and b
9 */
10 public int getSum(int a, int b) {
11 // Base case: when there's no carry left, return the result
12 if (b == 0) {
13 return a;
14 }
15
16 // Recursive case:
17 // 1. XOR gives the sum without considering carry bits
18 int sumWithoutCarry = a ^ b;
19
20 // 2. AND followed by left shift gives the carry bits
21 // (a & b) identifies positions where both bits are 1 (will generate carry)
22 // << 1 shifts these carry bits to their correct position (one bit left)
23 int carry = (a & b) << 1;
24
25 // 3. Recursively add the sum and carry until no carry remains
26 return getSum(sumWithoutCarry, carry);
27 }
28}
29
1class Solution {
2public:
3 int getSum(int a, int b) {
4 // Iterate until there's no carry left
5 while (b != 0) {
6 // Calculate carry bits by finding common set bits and shifting left
7 // Cast to unsigned to avoid undefined behavior with negative numbers
8 unsigned int carry = (static_cast<unsigned int>(a & b)) << 1;
9
10 // XOR gives sum without considering carry
11 a = a ^ b;
12
13 // Update b with carry for next iteration
14 b = carry;
15 }
16
17 // Return final sum when no carry remains
18 return a;
19 }
20};
21
1function getSum(a: number, b: number): number {
2 // Iterate until there's no carry left
3 while (b !== 0) {
4 // Calculate carry bits by finding common set bits and shifting left
5 // Use bitwise AND to find common set bits, then shift left by 1 to get carry
6 // Note: JavaScript/TypeScript automatically handles bitwise operations as 32-bit signed integers
7 const carry: number = (a & b) << 1;
8
9 // XOR gives sum without considering carry
10 // This performs addition without considering the carry bits
11 a = a ^ b;
12
13 // Update b with carry for next iteration
14 // The carry becomes the new b value for the next round of addition
15 b = carry;
16 }
17
18 // Return final sum when no carry remains
19 return a;
20}
21
Time and Space Complexity
Time Complexity: O(1)
The algorithm uses bitwise operations to add two integers without using the +
operator. The while loop iterates based on the number of carry operations needed. In the worst case for 32-bit integers, the carry can propagate at most 32 times (the bit width of the integers being processed). Since we're working with fixed 32-bit integers (masked by 0xFFFFFFFF
), the maximum number of iterations is constant and bounded by 32. Therefore, the time complexity is O(1)
.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space for the variables a
, b
, and carry
. No additional data structures are created that scale with input size. The space usage remains constant regardless of the input values, making the space complexity O(1)
.
Common Pitfalls
1. Forgetting to mask intermediate results in the loop
Pitfall: A common mistake is forgetting to mask the carry calculation with 0xFFFFFFFF
:
# WRONG - can cause overflow beyond 32 bits while b != 0: carry = (a & b) << 1 # Missing mask! a = a ^ b b = carry
Why it fails: Without masking, the left shift operation can create values larger than 32 bits, especially when dealing with negative numbers. This breaks the 32-bit arithmetic simulation and can cause infinite loops or incorrect results.
Solution: Always mask after the shift operation:
while b != 0: carry = ((a & b) << 1) & 0xFFFFFFFF # Correct - maintains 32-bit boundary a = a ^ b b = carry
2. Incorrect handling of negative number conversion
Pitfall: Using the wrong threshold or conversion method:
# WRONG - using wrong comparison if a >= 0x80000000: # Should check if it's greater than MAX_INT return ~(a ^ 0xFFFFFFFF) else: return a # OR WRONG - incorrect two's complement conversion if a > 0x7FFFFFFF: return -a # This doesn't properly convert two's complement
Why it fails: The value 0x7FFFFFFF
is the maximum positive 32-bit signed integer. Values greater than this represent negative numbers in two's complement. Using incorrect comparison or conversion will yield wrong results for negative numbers.
Solution: Use the correct threshold and conversion:
MAX_INT = 0x7FFFFFFF if a > MAX_INT: return ~(a ^ 0xFFFFFFFF) # Proper two's complement conversion else: return a
3. Not masking initial inputs
Pitfall: Operating directly on Python's arbitrary-precision integers:
# WRONG - doesn't handle negative numbers properly
def getSum(self, a: int, b: int) -> int:
while b != 0:
carry = (a & b) << 1
a = a ^ b
b = carry
return a
Why it fails: Python represents negative numbers differently than 32-bit two's complement. Without initial masking, negative inputs won't be handled correctly, leading to wrong results or infinite loops.
Solution: Always mask inputs at the beginning:
a = a & 0xFFFFFFFF b = b & 0xFFFFFFFF
4. Infinite loop with negative numbers
Pitfall: Not properly handling the termination condition when dealing with negative values:
# Can cause infinite loop with certain negative inputs while b: # Without proper masking throughout carry = (a & b) << 1 a, b = a ^ b, carry
Why it fails: When working with negative numbers in Python's representation, the carry can keep propagating indefinitely without the proper 32-bit boundary enforcement.
Solution: Ensure all operations stay within 32-bit bounds:
while b != 0: carry = ((a & b) << 1) & 0xFFFFFFFF a = (a ^ b) & 0xFFFFFFFF # Also mask the XOR result b = carry
5. Testing edge cases inadequately
Pitfall: Only testing with positive numbers or simple cases:
# Insufficient test cases assert getSum(1, 2) == 3 assert getSum(5, 7) == 12
Solution: Test comprehensive edge cases:
# Test positive + positive assert getSum(1, 2) == 3 # Test negative + negative assert getSum(-1, -2) == -3 # Test positive + negative assert getSum(1, -1) == 0 assert getSum(5, -3) == 2 # Test overflow boundaries assert getSum(2147483647, 1) == -2147483648 # MAX_INT + 1 # Test zero assert getSum(0, 0) == 0 assert getSum(5, 0) == 5
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!