用位运算实现加减乘除(不使用运算符)

题目描述

在不允许使用加减乘除运算符号的前提下,请实现加减乘除运算。

加法

第一步:异或运算:^
异或运算其实就是无进位相加的结果,相同为0,没有进位,不同为1。
第二步:与运算:& ,然后左移一位
与运算的结果,双1才会是1,其实就是找出了所有出现进位的地方,然后左移一位,就是进位后的地方。
第三步:前两步的运算结果相加,其实等同于原来两个数相加的结果
无进位相加的结果,加上进位数,就是相加的结果。因此,如果进位结果不是0,就继续重复前两步,直到进位结果为0,无进位相加的结果就是最终结果。
代码

    //用位运算实现加法
    public static int add(int a,int b){
        int sum = a;
        //如果没有进位,说明不用再加了,就是无进位相加的结果
        while (b!=0){
            //异或运算,求出无进位相加的结果
            sum = a^b;
            //与运算后左移一位,求出进位结果
            b = (a&b) << 1;
            //把无进位相加结果赋值给a,继续下一轮
            a=sum;
        }
        return sum;
    }

减法

减法其实很容易,计算机底层其实本来就只会加法,因此,减去一个数,就等于加上这个数的反码,一个数的反码就是他本身取反加1,因此,减法可以直接调用加法来实现。
代码

    //用位运算实现减法
    public static int minus(int a,int b){
        //一个数的反码就是他本身取反加1
        int fan=add(~b,1);

        //减去一个数就是加上这个数的反码
        return add(a,fan);
    }

乘法

先来想一下,手动在演草纸上算二进制乘法的话,应该怎么算?
比如:
0111 X 1001
模拟演草纸上手动演算:
用位运算实现加减乘除(不使用运算符)_第1张图片
观察手动演算的过程,再结合二进制的特点,二进制只有0和1,因此,0乘以任何数都是0,1乘以任何数都是他本身。因此,图中,如果设第一个乘数为a,第二个为b,那么,如果b最右边一位是1,第一行的结果就是a照抄,如果b的最右边结果为0,那么第一行就是全0,然后,再依次看b的右边第二个数,对应乘法的第二行,a整体左移一位,然后,依次累加。
代码

    //用位运算实现乘法
    public static int multi(int a,int b){
        //res累加和
        int res=0;
        //当b不为0时,也就是说还没乘完
        while (b!=0){
            //(b&1)取出最后一位,如果是1,就模拟a乘1,然后累加进res
            if((b&1)!=0){
                res=add(res,a);
            }
            //a左移一位,模拟乘法运算中的左移
            a=a<<1;
            //>>>是无符号右移,模拟乘法运算中,b的右边下一位
            b=b>>>1;
        }
        return res;
    }

除法

还是参考手动除法的过程,我们在手动除法运算的过程中,第一步是不是在寻找被除数的最高位?
用位运算实现加减乘除(不使用运算符)_第2张图片
参考图中我们一般的手动除法的过程,第一步,找最高位能够除的下的,图中画红叉的地方不行,因为就算上1,也比被除数大了,因此,只能在第二个最高位上3,3是最大的,上4就比被除数大了。这就是我们一般手动除法的过程,对吧?
那么,计算机中的除法,其实也是一样的原理,首先,让除数左移,每左移一位,相当于看看更高一位能不能被除的下,直到再左移就比被除数大的位置,就是结果最高位的数,类比于上图中手动除法结果的最高位的3。唯一的不同就是,手动除法计算的是10进制,是需要从1到9挨个尝试的,而计算机只有0和1,要么没有,要么就是它本身,因此,我们只需要一直左移,然后观察被除数减去移动后的数是否大于0,就可以模拟出尝试过程。
上述是对比手动除法的过程,模拟的位运算除法的思路,思路是没问题,不过,如何用代码实现,除数一直左移,然后判断是不是当前最大的且能够被被除数减去的值?
其实,代码的实现过程反而和思路不一样,我们可以让被除数右移,和除数左移其实是一样的思路,但是,却能避免溢出的问题。同时,采用遍历的思想,因为int一共也就32位,让被除数从31位开始遍历,依次右移31位到0位,然后,挨个判断是否移动后大于除数,因为是从31开始右移的,因此,第一次出现能够除的下的位置,一定是最高位,此时,累加最高位就行,然后模拟除法中,被除数减去除数右移那么多位,然后依次循环上述过程。
代码

    //a/b
    public static int div(int a,int b){
        //累计结果
        int res=0;
        //被除数右移来取代除数左移,避免溢出
        for (int i=31; i>-1; i--){
            //第一次进入if时,一定是结果的最高位
            if((a>>i)>=b){
                //累加最高位
                res |= (1<<i);
                //减去后,再进入下一轮
                a=minus(a,b<<i);
            }
        }
        return res;
    }

你可能感兴趣的:(算法学习,位运算,位图,运算符,加减乘除)