Facebook Pixel

371. Sum of Two Integers

MediumBit ManipulationMath
Leetcode Link

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:

  1. 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).

  2. 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)
  3. 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

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
  • Second iteration:
    • XOR: 001 ^ 100 = 101 (which is 5)
    • Carry: (001 & 100) << 1 = 000
  • 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. The sum without carry follows the XOR pattern: a ^ b gives us the result bit when we ignore carries

    • 0 ^ 0 = 0, 0 ^ 1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0
  2. 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

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 both a and b 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 bits
  • a ^ 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 is 10000000000000000000000000000000 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
  • Multiple iterations later, when carries propagate through...
  • Final result: 0 (as expected for 1 + (-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 Evaluator

Example 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
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

What data structure does Breadth-first search typically uses to store intermediate states?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More