Processing math: 100%

Ty Scales

Bitwise Addition

Here is a function that can add two numbers using bitwise operators.

func bitwise_add(a, b int) int {
    if b == 0 {
        return a
    }
    return bitwise_add(a^b, (a&b) << 1)
}

Why does this work? It’s helpful to look at this function not as performing “addition”, but in using the bitwise operators to rewrite a mathematical expression.

Let’s show this concretely with two numbers.

Let a = 5 as the expression 22+20
Let b = 7 as the expression 22+21+20
Let the sum of 5 and 7 be represented the expression 22+22+21+20+20

How can this expression be simplified? Whenever we encounter two exponents raised to the same power, rewrite the expression such that 2n+2n=2(n+1).

How do we represent that as code? We need the terms that appear in both a AND b.

// create a new expression that represents the like terms of both a and b
likeTerms := a&b

The like terms between a and b are 22 and 20. The new expression likeTerms represents 22+20 and we want to turn that in to 23+21. There’s a bitwise operator for that. It’s the bitwise SHIFT operator.

likeTerms := a&b
// for each exponent in likeTerms, shift 2^n to 2^(n + 1)
newExpressionB := likeTerms << 1

newExpressionB represents 23+21 and can now be added to the remaining terms from a and b that were not merged together. In this case, 21 from expression b is the only one exclusive to either expression. How do we extract it from the original expressions? We create a new expression uniqueTerms that contains the exponents exclusive to a and exclusive to b. There’s a bitwise operator for that. Its the XOR operator.

// create and expression of the terms that were exclusive to a and b, the terms that didn't get rewritten in to newExpressionB
uniqueTerms := a^b

we now have two new expressions. newExpressionB representing 23+21 and uniqueTerms representing 21 that we need to add together. We have a function for that. It’s the bitwise_add function that we’ve been crafting this entire time! Keep passing the new expressions recursively, until a consists of all unique terms and b is zero. At that point, we have obtained the answer.

Here are the remaining steps.
Rewrite 23+21+21 as 23+22
All terms are unique: 7+5=23+22=12

func bitwise_add(a, b int) int {
    if b == 0 {
        return a
    }

    // find the unique terms(bits) between a and b
    uniqueTerms := a^b

    // find the like terms(bits) between a and b
    likeTerms := a&b

    // for each like term, shift exponent from 2^n to 2^(n + 1)
    newExpressionB := likeTerms << 1
  
    // call bitwise_add until there are no like terms, return a
    return bitwise_add(uniqueTerms, newExpressionB)
}