Calculate the sum of two integers , but you are not allowed to use the operator + and -
Example:
Given a = 1 and b = 2, return 3.
既然不能用加法和减法,那么我们只能想到用位运算了,即与(&)或(|)非(~)异或(^)。
考虑两个整数(例如20,30)的二进制形式相加。这和我们小学学过的加法是相同的,都是从低位加起向高位进位,只不过从十进制变成了二进制,例如 10100 + 11110 = 110010.
我们想能否用位运算代替加法和减法,加法中基本会产生两个部分,进位和部分和。例如1+1,部分和为0, 进位为1. 不过这样一位一位的考虑太蠢了,我们寻求整体考虑的方法。
二进制加法中,我们需要满足如下的条件:
- 如果两个数的第n位都为1,则该位的部分和为0, 进位为1。
- 如果两个数的第n位都为0,则该位的部分和为0, 进位为0。
- 如果两个数的第n位为0和1,则该位的部分和为1, 进位为0。
稍微思考一下,我们就可以想到部分和可以用a ^ b表示,而进位可以用a & b表示。
但是我们如何将进位和部分和“加”起来呢,想想我们在加法中做的事情,是不是将低位向高位加,二进制情况下,我们是不是可以将进位左移一位在和部分和进行或操作呢?我们看一个例子:
101 + 001,部分和x = 100B, 进位y = 001B, x | (y<<1) = 110B.
看起来结果好像是对的哈,很遗憾我们换一个数字这种方法就不对了,例如20+30, 我们的算法给出的结果是42,这显然是不对的。 20 + 30 = 10100 + 11110,此时的部分和x = 01010B, 此时的进位y = 10100B,y<<1 = 101000B, 这样我们就看出来问题了,进位左移一位的结果和部分和在第四位二进制位的时候同为1,我们将它们或起来的话会损失数值!
那么怎么解决呢?聪明的你是不是已经想到了?我们只需要保证部分和与进位左移一位的值在任意一个二进制位上不同为1即可。那么如果它们不满足这个条件,我们部分和相应的冲突位置置0,将进位的冲突位置左移一位。为什么呢?思考!思考不能代劳。
a = 01010B的第四位和b = 101000B的第四位冲突,我们做如下操作:
- 用c = a & b取出冲突位。
- 用a = c ^ a将部分和的冲突位置0。
- 用b = c ^ b将进位的冲突位置0。
- 用b = b | c<<1生成新的进位。
- 若b&a不为0,则部分和和进位还有冲突,转到第1步执行;否则返回 a | b,即将部分和与进位结合在一起。
代码如下:
int getSum(int a, int b) {
int part_sum = a^b;
int carry = (a&b)<<1;
while(carry&part_sum){
int c2 = part_sum & carry;
part_sum ^= c2;
carry ^= c2;
carry |= (c2<<1);
}
return part_sum | carry;
}