这是一道算法题,当然也需要理解位运算才能完成。当不能使用 + - * / 来实现加法时,可以使用位运算来实现。
基本位运算符号
& 与,将两个二进制数按位相与,只有同时为1结果才为1
| 或,将两个二进制数按位相或,只要至少有一个为1则结果为1
^ 异或,将两个二进制数按位相异或,只有两个位相异结果才为1,两个位同为1或同为0结果为0
<< 有符号左移 <<< 无符号左移
>> 有符号右移 >>> 无符号右移
注:
- 符号位也是可以参与位运算的,0 代表正数,1 代表负数
- 负数的二进制 = 对应正数的补码,即正数原码取反 + 1
实现代码
/**
* @param {number} a
* @param {number} b
* @return {number}
*/
var add = function(a, b) {
if(a == 0) return b
if(b == 0) return a
return add((a^b),(a&b) << 1)
};
代码解释
a^b 相当于无进位求和,比如 1^9=8 (1001 ^ 0001 = 1000),这里的第一位(最右位)如果是正常求和,则 1+1 应该进一位到第二位,但它无视掉了进位的结果。
(a&b)<< 1 相当于求每一位的进位数,比如(1&9)<< 1 = 2 ((1001 & 0001)<< 1 = 0010),刚刚好得到的就是 a^b 过程中被无视掉的那些进位(本例只涉及一处进位)。
所以 add((a^b),(a&b) << 1) 就相当于 (a^b) + (a&b) << 1,将 两个数无进位求和结果 + 每一次求和得到的进位 。
上述代码就是在不断重复这个过程,直到进位数为0为止,则得到结果。
此外,注意 << 是有符号左移,下面举个例子方便理解。
比如 1 + (-2)求和。
1 ^ (-2)等价于 0001 ^ 1110 = 1111
1 & (-2)<< 1 等价于 (0001 & 1110) << 1 = 0
所以当第一次运算结果传入add函数时,因为此时b形参已经等于0,所以返回 1111 即 -1 (符号位从哪里开始应该底层有记录)