转载自:https://blog.youkuaiyun.com/sinat_35261315/article/details/78376690
Divide Two Integers
原题链接Divide Two Integers
加法
通过异或运算和与运算实现
两个二进制数相加,异或运算的结果是不考虑进位时的结果
两个二进制数相加,与运算的结果是对应为是否有进位
0101 + 0001 = 0110
0101 ^ 0001 = 0100
0101 & 0001 = 0001
异或结果表明,如果不考虑进位,那么结果为0100
与运算结果表明,最低位需要向次低位进1
算式改为0100 + 00010(与结果左移一位,将进位加到高位上)
计算结果0101
(a^b)是为了计算不加进位的部分的和,(a&b)<<1是计算进位的部分,然后下一次a^b的时候会将进完位的部分与之前没有进位的部分加和。什么时候b才会等于0?只有不能再进位的时候b才会等于0,此时应该是a先等于0,也就是b其实就是最后的总和了,然后a=0,a=a^b=0+b,然后b=0&b=0,之后就返回a了。
代码实现
int add(int a, int b)
{
return b == 0 ? a : add(a ^ b, (a & b) << 1);
}
减法
同加法,减去一个数等于加一个数的相反数
int negative(int n)
{
return add(~n, 1);
}
int sub(int a, int b)
{
return add(a, negative(b));
}
乘法
笔算二进制乘法
0101 a
× 0110 b
----------------
0000
0101
0101
+ 0000
-------------
00011110
b的每一位乘a,左移一定位数后加到结果上,代码把逻辑实现出来就可以了。可以在代码中每加一次结果就将a左移一位,就不需要每次算完都左移。最好考虑符号问题,乘之前都转为正数
int get_sign(int n)
{
if(n >> 31)
return 1;
else
return 0;
}
int positive(int n)
{
if(n >> 31)
return negative(n);
else
return n;
}
int multi(int a, int b)
{
bool be_negative = false;
if(get_sign(a) ^ get_sign(b))
be_negative = true;
unsigned int x = positive(a);
unsigned int y = positive(b);
int n = 0;
while(y | 0)
{
if(y & 1)
n = add(n, x);
x = x << 1;
y = y >> 1;
}
return be_negative ? negative(n) : n;
}
除法
第一种方法,每次循环都是a-b,看能减多少次,如果a很大b很小,效率比较低
第二种方法,从最大倍数n开始,看看a / n 是否大于b,如果大,将n加到结果上,然后缩小倍数继续循环
int div(int a, int b)
{
bool be_negative = false;
if(get_sign(a) ^ get_sign(b))
be_negative = true;
unsigned int x = positive(a);
unsigned int y = positive(b);
int res = 0;
int i = 31;
while(i >= 0)
{
if((x >> i) >= y)
{
res = add(res, 1 << i);
x = sub(x, y << i);
}
/* 也可以在else里,因为这里是int型,最大也就1 << 31倍 */
i = sub(i, 1);
}
/* 题中要求溢出时为INT_MAX,实现时可以让它溢出 */
//if(res < 0 && !be_negative)
// return INT_MAX;
return be_negative ? negative(res) : res;
}