【题目】
不使用加减乘除运算符号实现四则运算。
【分析】
1.加法
既然不能使用运算符,那么我们只能想到使用逻辑运算,首先想到的就是《体系结构》和《数字电路》课程中学习的加法器的设计了,首先来看1位数加法是如何实现的。
我们有如下的逻辑运算表:
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 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);//补码等于反码加1
return 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等于加了2a
b = 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);
}