题目描述:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
题目很简单,但解决起来还是有一定难度的,说起代替加减乘除的方式,一下子能想起来的有sizeof,二进制的位运算。
sizeof行不通之后,又琢磨位运算中的异或,1 ^ 1->0, 1 ^ 0->1, 0 ^ 1->1, 0 ^ 0->0。这个规则看起来最接近加法法则,但是缺乏进位,比如,我们如何区分1 ^ 1 和0 ^ 0带来的结果呢,前者有进位而后者没有,那么如何给1 ^ 1 标志出进位呢?
答案是&,按位与。只有1 & 1结果为1。我们又知道加法中进位的含义就是例如:16+15,6+5结果超过10,所以在前一位加1,结果为31。在二进制中也是一样,所以我们需要把代替进位的&运算结果先左移一位,再与异或运算的结果按位相加。
这里你又会问:不是程序中不能出现加法运算吗?那要怎么实现进位(num1&num2)<<1和不含进位的和num1^num2的相加呢?
答案是使用递归或者循环结构。递归就是在函数体内再次调用此函数,循环就是将同样的加法和进位程序写入循环体内,多次执行。两者的结束条件都是一样的,想一想,什么时候我们就不需要进行加法计算了呢?那就是在进位为0的时候,此时num1^num2就是最后的结果了。
递归程序如下:首先给carry和pre_sum赋初值,这两步也是后续递归时不断执行的操作。由于这个程序的本质还是要执行加法,所以最后返回的应该是pre_sum。
int Add(int num1, int num2){
int carry=(num1&num2)<<1;//与运算
int pre_sum=num1^num2;//异或运算
//这里不能用while,为什么?
//因为如果用while,则一直使用的是第一次的carry初值,不会跳出循环
//除非在循环里改变carry的值
if(carry!=0){
pre_sum=Add(carry, pre_sum);
}
return pre_sum;
}
另一种是循环:由此也可以看出递归和循环是如何等价的。递归语句之外的其他操作是会被重复执行的,而循环结构内的语句也是不断执行的。
int Add(int num1, int num2){
//也可以使用while循环代替递归
int carry=(num1&num2)<<1;//与运算
int pre_sum=num1^num2;//异或运算
while(carry!=0){
int temp=carry;
carry=(carry&pre_sum)<<1;
pre_sum=temp^pre_sum;
}
return pre_sum;
}
int Add(int num1, int num2){
//再次简化,num1:carry; num2:pre_sum
while(num1!=0){
int temp=num1;
num1=(num1&num2)<<1;
num2=temp^num2;
}
return num2;
}
其实还是很难懂的,这里趁热记下来,方便以后的复习和思维锻炼。