溢出避免:求平均数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/restlessssh/article/details/69950621

好久不见……
这次来说说求平均数的问题。

问题很简单,就是求 (a+b)/2。
当然,我不是让你直接printf(“%d”,(a+b)/2); 这样随便一个人学一小时C语言就可以做到。
问题是 a+b 如果溢出了怎么办?溢出的话就得不到正确结果了,这样该怎么解决?
如果简单的避免溢出,将程序改成 a/2+b/2,这样也是不对的,这样会在a,b都为奇数时导致缺失。
还有人会给出 a+(b-a)/2,其实这种办法也是治标不治本,因为加和减是一样的,既然加法会溢出,那么同样,减法也会溢出,所以这种办法也是没有意义的。

说说我的想法吧,首先举个例子: (193+235)/2,对这两个数,我们已知计算时会发生上溢出,所以可以尝试 a+(b-a)/2这个公式,但是这个公式仅适用于减法不会下溢出的情况下。虽然这个公式并不是正确的,但是我们可以从公式中得到一些东西:
(193+235)/2 => (193+193+42)/2 => 193+(42)/2
其中,42就是235-193
我们可以把193+(42)/2看作是193+(42+0)/2,也可以看作是192+(43+1)/2191+(44+2)/2,…………
我的意图已经很明显了,就是将两个数字都分成相等和不相等的两个部分,我们只要保证不相等的部分的和不溢出就行了。
那么,我们该如何保证不相等的部分的和不溢出呢?

最极端的想法:只要没有进位,就不会溢出。

不产生进位的想法虽然比较极端,但是用在这里恰到好处,因为我们有‘异或’运算。
我们可以看看‘异或’的作用。
193^235 =>11000001^11101011 = 00101010
就相当于把两个数相等的位全部置0,然后不相等的位进行相加,由于相加的位不会出现两个1的情况,所以不会产生进位,也就不会溢出。
到现在为止,我们已经得到了不相等部分的和并且保证不会溢出,就剩下相等的部分了。
不相等的部分是将两个数相等的位置0,而相等的情况就是将两个数相等的位保留下来,并将不相等的位置0,这个就用‘与’运算。
于是,我们的算法就写出来了:

average = (a&b)+((a^b)>>1)

其中,a&b就是相等的部分,而a^b则是不相等的部分。
但是,这样还是不完全对的,它只适用于无符号数。右移操作直接导致了这个算法的结果是向下取整的,也就是说,如果a,b为有符号数,当a+b为1时,(a+b)/2为0,而当a+b为-1时,(a+b)/2为-1。
可以看出结果还需要调整。当a+b小于0且是奇数时,我们需要对结果+1进行修正。
((temp>>31)&1&(a^b)) 实现了对计算结果的修正,当a+b大于等于0时,值为0,当a+b小于0时,值为1。(中间的那个&1不能省掉,因为temp是有符号数,当temp小于0时,temp>>31的值为0xffffffff,而不是1。)

这样,我们的有符号数的算法就有了:

temp = (a&b)+((a^b)>>1)
average = temp + ((temp>>31)&1&(a^b))

求平均数的算法就到此为止了。

祝身边的朋友找到好工作,祝我喜欢过的女孩每天开心,祝我的亲人健康快乐,也祝我和我的研友能考上理想的学校。

我是算法吹,以后会给大家带来更多精彩的算法。算法吹

展开阅读全文

没有更多推荐了,返回首页