029位运算实现加减乘除

位运算实现加减乘除

加法

a + b = a ^ b + 进位信息

可能相加后还有进位,就一直加到没有进位再停止

public int add(int a, int b){
    int ans = a;
    while (b != 0){//b既是第二个加数,也是进位信息
        ans = a ^ b;//不能直接写a = a ^ b,因为下一步还要用到a
        //a和b做与运算后左移一位,可以得到进位信息
        b = (a & b) << 1;
        //a更新为ans,如果还有进位信息,就继续与进位信息相加
        a = ans;
    }
    return ans;
}

减法

a - b = a + (-b)

-b = ~b + 1

public int minus(int a, int b){
    return add(a, neg(b));
}

public int neg(int n){
    return add(~n, 1);
}

乘法

//a乘以被乘数的某一位,然后a左移,继续乘下一位
public int multiply(int a, int b){
    int ans = 0;
    while (b != 0){//被乘数还没有乘完
        //被乘数当前位不为0
        if((b & 1) != 0){
            ans = add(ans, a);
        }
        // 乘数左移
        a <<= 1;
        // 被乘数右移,被乘数要相乘的那一位与a对齐,这样就可以判断b的最后一位是否为0
        b >>>= 1;
    }
    return ans;
}

除法

非负数情况下:

\(280 = 25*2^3 + 25*2^1+25*2^0 ... 5\)

对于int,31位是符号位,从\(2^{30}\)开始判断这个数是否包含\(2^{30}\),如果有的话,继续用剩下的余数,判断29、28、一直往下判断

例如对于280,前面的数都太大了,一直到\(25*2^8\),280大于25*2^8$,然后280-\(25*2^8\)=80.

继续用80来判断,80包含了\(25*2^1\),然后余数30

30包含了\(25*2^0\),余数是5,5小于25,结束

得到280/25 = 1011

对于被除数y,\(y*2^{30}\),也就是y左移30位,如果y这个数稍微大一点,会导致y移除,超出int的范围

public int div(int a, int b){
    //如果a和b小于0,就转成非负数
    //最小负整数不能转,它没有对应的相反数,例如8bit,范围是-128~127
    int x = a < 0 ? neg(a) : a;
    int y = b < 0 ? neg(b) : b;
    int ans = 0;
    for (int i = 30; i>= 0; i = minus(i, 1)){
        //x/y,要判断x是否大于等于y*(2^i),从比较大的数开始判断,x中是否包含这个比较大的数
        //但是y进行左移容易造成溢出
        //等同于x右移i位,如果大于等于y,就说明x大于等于y*(2^i)
        if((x >> i) >= y){
            ans |= (1 << i);//答案的i位置为1
            x = minus(x, y << i);//余数
        }
    }
    //只有ab异号时,答案才是负数
    //-280/25
    //280/-25
    //-280/-25
    return a < 0 ^ b < 0 ? neg(ans) : ans;
}

讨论了整数最小值的除法

public int devide(int a, int b){
    if(a == MIN && b == MIN){
        return 1;
    }
    if (a != MIN && b != MIN){
        return div(a, b);
    }
    //a不是最小,b是最小
    if (b == MIN){
        return 0;
    }
    //a是最小,b是-1,返回整数最大(题目要求)
    if (b == neg(1)){
        return Integer.MAX_VALUE;
    }
    //a是整数最小,b不是最小也不是-1
    //如果b是正数,那么a+b使得a没有那么小,a/b = (a+b)/b-1
    //如果b是负数,那么a-b使得a没有那么小,a/b = (a-b)/b+1
    a = add(a, b > 0 ? b :neg(b));
    int ans = div(a, b);
    int offset = b > 0 ? neg(1) : 1;
    return add(ans, offset);
}
posted @ 2023-10-16 17:28  平平无奇的five  阅读(49)  评论(0)    收藏  举报