1.加法
不考虑进位情况下,位的异或运算跟*求’和’*的结果一致:
异或 1^1=0 1^0=1 0^0=0
求和 1+1=0 1+0=1 0+0=0
位的与运算跟*求’进位‘*的结果也是一致:
位与 1&1=1 1&0=0 0&0=0
进位 1+1=1 1+0=0 0+0=0
思想:
计算a+b
stp1:算出两数不考虑进位的值sum,和只考虑进位的值carry
stp2:如果进位值不为零,就a=sum,b=carry,然后重复stp1,直到carry==0
int Add(int num1, int num2) {
int n1,n2;
n1=(num1&num2)<<1;
n2=num1^num2;
while(n1&n2) {
num1=n1;num2=n2;
n1=(num1&num2)<<1;
n2=num1^num2;
}
return n1|n2;
}
递归实现
2.减法
a-b=a+(-b)
求一个数的负的操作是将其连符号位一起取反然后加1
int subtraction(int a, int b) {
return add(a, add(~b, 1));
}
3.乘法
乘法运算可以转化为加法运算,但是需要考虑符号问题。
1)计算绝对值乘积;
2)根据同正异负,确定符号位。
int multiply(int a, int b){
// 取绝对值
int multiplicand = a < 0 ? add(~a, 1) : a;
int multiplier = b < 0 ? add(~b , 1) : b;// 如果为负则取反加一得其补码,即正数
// 计算绝对值的乘积
int product = 0;
int count = 0;
while(count < multiplier) {
product = add(product, multiplicand);
count = add(count, 1);// 这里可别用count++,都说了这里是位运算实现加法
}
// 确定乘积的符号
if((a ^ b) < 0) {// 只考虑最高位,如果a,b异号,则异或后最高位为1;如果同号,则异或后最高位为0;
product = add(~product, 1);
}
return product;
}
第二种思路:二进制乘法!
这一过程就是根据乘数的每一位为0或1时,将被乘数错位的加在积上。时间复杂度为O(logN)。
(1) 判断乘数是否为0,为0跳转至步骤(4)
(2) 将乘数与1作与运算,确定末尾位为1还是为0,如果为1,则相加数为当前被乘数;如果为0,则相加数为0;将相加数加到最终结果中;
(3) 被乘数左移一位,乘数右移一位;回到步骤(1)
(4) 确定符号位,输出结果;
int multiply(int a, int b) {
//将乘数和被乘数都取绝对值
int multiplicand = a < 0 ? add(~a, 1) : a;
int multiplier = b < 0 ? add(~b , 1) : b;
//计算绝对值的乘积
int product = 0;
while(multiplier > 0) {
if((multiplier & 0x1) > 0) {// 每次考察乘数的最后一位
product = add(product, multiplicand);
}
multiplicand = multiplicand << 1;// 每运算一次,被乘数要左移一位
multiplier = multiplier >> 1;// 每运算一次,乘数要右移一位(可对照上图理解)
}
//计算乘积的符号
if((a ^ b) < 0) {
product = add(~product, 1);
}
return product;
}
4.除法
除法运算转换成减法运算,即不停的用除数去减被除数,直到被除数小于除数时,此时所减的次数就是我们需要的商,而此时的被除数就是余数。这里需要注意的是符号的确定,商的符号和乘法运算中乘积的符号确定一样,即取决于除数和被除数,同号为证,异号为负;余数的符号和被除数一样。
int division(int a, int b)
{
if (b == 0)return 0;
bool flag = true;
if ((a^b) > 0) flag = false;
a = (a < 0) ? add(~a, 1) : a;
b = (b < 0) ? add(~b, 1) : b;
int n = 0;
a = subtraction(a, b);
while (a >= 0)
{
n = add(n, 1);
a = subtraction(a, b);
}
if (flag)
return add(~n, 1);
return n;
}
采用类似二分法的思路,从除数*最大倍数开始测试,如果比被除数小,则要减去,下一回让除数的倍数减少为上一次的一半,当倍数为1时,就把被除数中所有的除数减去,并得到商。时间复杂度优化到O(logN)。
计算机是一个二元的世界,所有的int型数据都可以用[2^0, 21,…,231]这样一组基来表示(int型最高31位)。不难想到用除数的231,230,…,22,21,2^0倍尝试去减被除数,如果减得动,则把相应的倍数加到商中;如果减不动,则依次尝试更小的倍数。这样就可以快速逼近最终的结果。
2的i次方其实就相当于左移i位,为什么从31位开始呢?因为int型数据最大值就是2^31啊。
int divide(int dividend, int divisor) {
bool flag = true;
if ((dividend^divisor)>0) //积的符号判定
flag = false;
int x = (dividend < 0) ? add(~dividend, 1) : dividend;
int y = (divisor < 0) ? add(~divisor, 1) : divisor;
int ans = 0;
int i = 31;
while (i >= 0)
{
//比较x是否大于y*(1<<i)=(y<<i),避免直接比较,因为不确定y*(1<<i)是否溢出
if ((x >> i) >= y) //如果够减
{
ans = add(ans, (1 << i));
x = subtraction(x, (y << i));
}
i = subtraction(i, 1);
}
if (flag)
return add(~ans, 1);
return ans;
}