- 题目
位运算来实现加减乘除,中间过程不能有任何+、-、*、/的使用
- 加法
对两个数异或实际结果为没有进位的加法结果,对两个数想与再左移一位结果为进位的结果
比如: 0101 + 0111,实际结果为5+7=12
function add(a, b) {
while (b != 0) {
sum = a ^ b; // 无进位加结果
b = (a & b) << 1; //进位结果
a = sum;
}
return sum;
}
- 减法
减法实际上 a-b 为 a+b 的相反数,在计算机中求一个数的相反数为取反后+1
function mini(a, b) {
function contrary(n) {
return add(~n, 1); // 因为不能存在+运算符,使用之前的加法函数
}
return add(a, contrary(b));
}
- 乘法
两个二进制相乘,同样可以使用运算十进制运算方式,比如 0111 * 0101,实际结果为 7*5=35
function multi(a, b) {
// b每次乘完右移一位,当b所有位都是0的时候退出
let res = 0;
while (b != 0) {
// b & 0001,即b的最后一位(当前要乘的位)不是0,加上a当前左移后的结果
if ((b & 1) != 0) {
res = add(res, a);
}
a = a << 1; // a左移满足乘法中根据当前位偏移的计算
b = b >>> 1; //注意是无符号右移,否则高位会补1变成负数
}
return res;
}
- 除法(只考虑两个正数除法)
除法通过乘法进行倒推,比如乘法的例子 0111 * 0101 结果为 100011,即 100011 / 0111 结果为 0101
从乘法的计算过程中可以看出,最终的结果是通过0111不断左移后累加的结果,也就是说100011 / 0111 的结果中的最高位的1,将0111偏移到该位置的结果,一定比100011小,再往左偏移一位一定比100011大,也就是说将 0111 向左偏移 n 位得到的比100011小,最接近100011的偏移量n,一定是相除结果的最高位
将0111向左偏移2位,011100小于100011,再偏移一位就会大于100011,说明偏移2位是最接近100011的,所以相除最终结果最高位一定在第3位上 1 << 2, 0100
此时得到相除结果最高位,因为乘法中是0111不断左移后累加的结果,将100011 - 011100(偏移后的结果),进而转求剩下的结果中的最高位,剩下结果为000111,而因为上一步求出了第三位为1,当前在求第二位结果,因为000111和0111相等,说明不能偏移,即第二位为0,得到0100
求最后一位,剩下的结果为000111,0111和它相等,所以最后一位为1
// a是除数、b是被除数, a / b
function divide(a, b) {
let res = 0;
// 因为a是b左移累加的结果,所以可以从a的最高位开始找,如果找到比b大的,说明该位就是b右移最接近a的偏移量
for (let i = 31; i > -1; i--) {
if (a >>> i >= b) {
res |= 1 << i;
a = mini(a, b << i); // 减去b最高位右移的结果,求剩余的最高位
}
}
}