使用位运算计算加减乘除四则运算

来源:互联网 发布:java面试造假 编辑:程序博客网 时间:2024/06/05 10:24

【题目】
不使用加减乘除运算符号实现四则运算。

【分析】

1.加法

既然不能使用运算符,那么我们只能想到使用逻辑运算,首先想到的就是《体系结构》和《数字电路》课程中学习的加法器的设计了,首先来看1位数加法是如何实现的。

我们有如下的逻辑运算表:

000011101110我们发现这其实就是逻辑运算中的异或运算,然而算到这里我们仅仅是算出来没有进位的情况下的和,那么有进位的情况呢,比如1+1=10,我们不难发现如下的真值表:

000010100111其实就是一个与运算,于是我们就可以用数字电路中的一个异或门和一个与门实现这个1位加法器,如下所示:


那么我们考虑在程序中如何实现这个情况呢?我们发现其实进位的话我们使其向左移位就能实现了,于是我们有对于1位加法:

int add_one(int a,int b){int ans = a^b;int carry = (a&b)<<1;return ans^carry;}
那么下面我们将此情况扩展多多位数的情况,由1位数的情况我们知道第一位进位应该加到第二位上,第二位的进位加到第三位上,那么我们可以使用一个循环将所有的进位都加到和上面,类似于将上面的1位加法器串联,这其实也是加法器的实现原理,那么我们可以很自然的写出来程序:

int add(int a,int b){int ans = a;while(b){ans = a^b;b = (a&b)<<1;a = ans;}return ans;}
2.减法

其实我们在《组成原理》里面已经学到,对于减法运算其实还是加法,就是等于加上除数的补码,那么我们知道补码的求法就是该数的反码加1,那么很容易的我们有了下面的代码:

int minus(int a, int b){int y = add(~b,1);//补码等于反码加1return add(a,y);}

3.乘法

a*b就是将b个a相加,但是现在我们先考虑b为正数的情况,负数的话我们只需要判断最终的符号之后再按正数求解即可。

那么乘法就是实现b个a相加,我们可以直接利用上面的加法程序,可以实现一个比较直观的方法:

int multiply(int a, int b){int ans = 0;while(b){ans = add(ans,a);b = add(b,-1);}return ans;}
但是我们这样做的话,对于b的计算可能会占用一点复杂度,那么我们有没有更快更简洁的实现方法呢,答案是肯定的,我们先来看下面的代码:

int multiply(int a, int b){int ans = 0;while(b){if( b&1){ans =add(ans,a);}a = a << 1;//等于将a扩大了两倍,加a等于加了2ab = b >> 1;//等于将b缩小了两倍}return ans;}
上面我们其实采用的是成2倍速度增长的,如果b除以2如果还等于1,那么则可以加上上一次的a的两倍,这样的话运算次数就会比第一种方法少很多。

4.除法(求模值与余数)

其实这道题在我上学期去面百度的时候被问到过,当时完全没有思路,然后面试就悲剧了。其实这道题有两种解法,一种是二分查找,一种就是利用逻辑运算,今天只讲逻辑运算,下次再写二分查找,把二分查找的适用情况都讲一下。

好了,废话不说了,看除法是如何做的呢?
其实除法就是乘法的逆过程,乘法是不断的加,那么除法就是不断的减去除数,下面就像代码给出:

pair<int ,int> divide(int a,int b){int factor = 0;int r = 0;int i = 0;for(i = 0; i<32;i++){if( (a>>i) >=b )  //避免溢出        {              factor = factor | (1<<i);              a -= (b<<i);          }}r = a;return make_pair(factor,r);}

0 0