平均值的四种求法

标题 ##求两个数的平均值

1.存在缺陷的算法(a+b)/2
分析:数学运算中一贯的算法是(a+b)/2,但在c语言中这种算法存在着一定的缺陷,当a和b足够大时,a和b的和就会存在溢出,从而得不到我们想要的结果。c语言中有相应的操作符可达到求平均数的效果,例如:>>(右移),&按为与,^(按位异或)。

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>
#include<windows.h>
int main()
{
    int a = 2147483647, b = 2147483647;
    int ret = 0;
    ret = (a + b) / 2;
    printf("ret=%d\n", ret);
        system("pause");
        return 0;
}

结果:
当我们将a和b的赋值为2147483647,输出的结果为-1;此时,a和b的和为2147483647的2倍,但是编译器中所存放的最大数为2147483647,故发生了溢出,输出的结果为-1。

2.不使用(a+b)/2的这种形式:

如果在汇编中的话,这种方法可以不产生高位溢出。

思路如下:
(x&y)+((x^y)>>1),把x和y里对应的每一位(指二进制位)都分成三类,每一类分别计算平均值,最后汇总。其中,一类是x,y对应位都是1,用x&y计算其平均值;一类是x,y中对应位有且只有一位是1,用(x^y)>>1计算其平均值;还有一另是x,y中对应位均为0,不用计算。

下面我再分别说明一下前两种情况是怎样计算的: 第一部分:x,y对应位均为1,相加后再除以2还是原来的数,例如两个00001111相加后除以2仍得00001111
第二部分:对应位有且只有一位为1,用“异或”运算提取出来,然后>>1(右移一位,相当于除以2),即到到第二部分的平均值。
第三部分:对应位均为零,因为相加后再除以二还是0,所以不用计算。
三部分汇总之后就是(x&y)+((x^y)>>1)

解释前面说到可以避免溢出:
假设x,y均为unsigned char型数据(0~255,占用一字节),显然,x,y的平均数也在0~255之间,但如果直接x+y可能会使结果大于255,这就产生溢出,虽然最终结果在255之内,但过程中需要额外处理溢出的那一位,在汇编中就需要考虑这种高位溢出的情况,如果(x&y)+((x^y)>>1)计算则不会。
值得注意的是:
整形存储的时候其实本质就是按照二进制存储的,可以直接进行操作;其他类型的,例如浮点型,用科学计数法存储,二进制存储的是有效数字和次方,按位运算毫无意义,所以计算平均值时用int不能用float,这样导致平均值的小数位就没有了。

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<windows.h>
int main()
{
    int a = 0;
    int b = 0;
    int average;
    printf("请输入两个数");
    scanf("%d%d", &a, &b);
    average = (a&b) + ((a^b) >> 1);
    printf("%d", average);
    system("pause");
    return 0;
}

3.防止溢出的算法 a+(b-a)/2

这个程序首先将b与a的差值求出并且除以2,在加上a;这样的算法保证了程序在运算过程中不会发生溢出现象。即使当a和b的值较大时,也会求得平均数。


#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>
#include<windows.h>
int main()
{
        //int a = 2147483647, b = 2147483647;
        int a=2,b=4;
        int ret = 0;
        ret = a + (b - a) / 2;
        printf("ret=%d\n", ret);
        system("pause");
        return 0;
}

4.运用>>右移操作符

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>
#include<windows.h>
int main()
{
    int a = 2, b = 8;
    int ret = 0;
    ret = (a + b) >> 1;
    printf("ret=%d\n", ret);
    system("pause");
    return 0;
}

此程序利用右移操作符来达到除2的目的以此来求得a和b的平均数。右移操作符是将一个数的所有位向右移动若干位,在这里我们需要将a和b的和向右移动1位,其高位来到低位,从而达到除2的目的。

阅读更多

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