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)
}