位运算实现加、减、乘、除
一、加法
原理:\(a\) + \(b\) = 无进位加法 + 进位加法 = \(a\)^\(b\) + (\(a\) & \(b\)) << \(1\) = ...
int add(int a, int b) {
while (b != 0) {
int no_c = a ^ b; // 无进位加法
int c = (unsigned int)(a & b) << 1; // 进位加法,防止越界
a = no_c;
b = c;
}
return a;
}
二、减法
原理:转换为加法实现,\(a\) - \(b\) = \(a\) + \((\)-\(b\)\()\),在计算机中,整数通过补码的形式储存,而补码的相反数等于符号位取反,数值位取反加1,相当于整体取反加1,即 \(-b\) = ~\(b\) + \(1\),然后使用上方加法运算。
int sub(int a, int b) {
b = add(~b, 1); // ~b + 1
return add(a, b); // a + (-b)
}
三、乘法
方法一(累加):\(a\) * \(b\) = \(b\) + \(b\) + ... + \(b\) (\(a\)个),需要注意的是 \(a\) 或 \(b\) 可能为负数,我们可以先计算两个乘积的绝对值,然后再根据两个数的符号位确定最终的符号。
int mul(int a, int b) {
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
if (absB < absA) swap(absA, absB); // 减小循环次数
int cnt = 0, res = 0;
while (cnt < absA) {
res = add(res, absA);
cnt = add(cnt, 1);
}
if ((a & 0x80000000) != (b & 0x80000000)) res = add(~res, 1);
return res;
}
方法二(二进制乘法):对于 \(5\) * \(7\),其二进制为 \(101\) * \(111\),相当于后面的每一位 \(1\) 乘上 \(101\) 然后左移相应位数加到答案上,需要注意的是两个数可能为负数,我们可以先计算两个乘积的绝对值,然后再根据两个数的符号位确定最终的符号。
int mul(int a, int b) {
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
if (absA < absB) swap(absA, absB); // 减小循环次数
int res = 0;
while (absB) {
if ((absB & 1) != 0)
res = add(res, absA);
absA <<= 1;
absB >>= 1;
}
if ((a & 0x80000000) != (b & 0x80000000)) res = add(~res, 1);
return res;
}
四、除法
方法一(累减):\(a\) / \(b\) = \(c\) ... \(d\) => \(a\) = \(b\) * \(c\) + \(d\),那么用 \(a\) 多次减去 \(b\) 使得结果小于 \(b\) 时,此时的次数就是 \(c\),结果就是 \(d\),和乘法类似,首先排除符号的影响,最后再考虑符号位。
int div(int a, int b) {
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
int res = 0;
while (absA >= absB) {
absA = sub(absA, absB);
res = add(res, 1);
}
if ((a & 0x80000000) != (b & 0x80000000)) res = add(~res, 1);
return res;
}
方法二:\(a\) / \(b\) = \(c\) => \(a\) = \(b\) * \(c\),假设:a = b * (2^7) + b * (2^4) + b * (2^1),则 \(c\) 的二进制一定是 10010010,因此可以转换成 \(a\) 是由几个 \(b * 2^i\) 的结果组成。
int div(int a, int b) {
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
int res = 0;
for (int i = 31; i >= 0; i = sub(i, 1) ) {
if ((absA >> i) >= absB) {
res |= (1 << i);
absA = sub(absA, absB << i);
}
}
if ((a & 0x80000000) != (b & 0x80000000)) res = add(~res, 1);
return res;
}
注意:若题目有溢出相关要求,根据要求处理溢出情况。
对于特殊情况,a = 0x80000000 && b != -1 && b != 0x80000000,则 \(a / b\) 应该通过如下方式来计算,先让 \(a + 1\),然后算 \((a + 1) / b = c\),接着 \(a - (b * c) = d\),然后 \(d / b = e\),最后 \(c + e = ((a + 1)/b) + ((a - (b * c)) / b) = a / b\),即得到 \(a / b\) 的值。
class Solution {
public:
int add(int a, int b) {
while (b != 0) {
int no_c = a ^ b; // 无进位加法
int c = (unsigned int)(a & b) << 1; // 进位加法,防止越界
a = no_c;
b = c;
}
return a;
}
int sub(int a, int b) {
b = add(~b, 1); // ~b + 1
return add(a, b); // a + (-b)
}
int mul(int a, int b) {
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
if (absA < absB) swap(absA, absB); // 减小循环次数
int res = 0;
while (absB) {
if ((absB & 1) != 0)
res = add(res, absA);
absA <<= 1;
absB >>= 1;
}
if ((a & 0x80000000) != (b & 0x80000000)) res = add(~res, 1);
return res;
}
int div(int a, int b) {
int absA = a < 0 ? add(~a, 1) : a;
int absB = b < 0 ? add(~b, 1) : b;
int res = 0;
for (int i = 31; i >= 0; i = sub(i, 1) ) {
if ((absA >> i) >= absB) {
res |= (1 << i);
absA = sub(absA, absB << i);
}
}
if ((a & 0x80000000) != (b & 0x80000000)) res = add(~res, 1);
return res;
}
int divide(int a, int b) {
if (a == 0 || (a != 0x80000000 && b == 0x80000000)) return 0;
if (a == 0x80000000) {
if (b == 1) return 0x80000000;
else if (b == -1) return 0x7fffffff;
else if (b == 0x80000000) return 1;
}
if (a == 0x80000000) {
int res = div(add(a, 1), b);
return add(res, div(sub(a, mul(res, b)), b));
}
return div(a, b);
}
};

浙公网安备 33010602011771号