更准确的平均方法是ARR[0]/N+ARR[1]/N+ARR[N-1]/N或(ARR[0]+ARR[1]..+ARR[N

What is more accurate way to average, ARR[0]/N+ARR[1]/N...+ARR[N-1]/N or (ARR[0]+ARR[1]...+ARR[N-1])/N in double?

本文关键字:N+ARR +ARR ARR N-1 方法      更新时间:2023-10-16

计算一组数字ARR[0]/N+ARR[1]/N...+ARR[N-1]/N(ARR[0]+ARR[1]...+ARR[N-1])/N的平均值的更准确方法是什么?(ARR是一组数字,N是该组数字的计数)

假设我有一组数字,每个数字的范围从0.01.0(它们是双\浮点数),有数千个甚至数百万个。

我对递归平均等新方法持开放态度(将双细胞平均到数组中,然后再次平均,直到输出一个细胞数组)。

如果接近零的值非常接近零,则在求和时会出现舍入问题(可能是向上或向下舍入错误),或者在对大量数字求和时,会出现任何数字范围的舍入问题。解决这个问题的一种方法是使用一个求和函数,该函数只将具有相同指数的数字相加(直到您调用getsum()来获得总和,它会使指数尽可能接近)。示例C++类(注意,代码是使用Visual Studio编译的,在uint64_t可用之前编写)。

//  SUM contains an array of 2048 IEEE 754 doubles, indexed by exponent,
//  used to minimize rounding / truncation issues when doing
//  a large number of summations
class SUM{
    double asum[2048];
public:
    SUM(){for(int i = 0; i < 2048; i++)asum[i] = 0.;}
    void clear(){for(int i = 0; i < 2048; i++)asum[i] = 0.;}
//  getsum returns the current sum of the array
    double getsum(){double d = 0.; for(int i = 0; i < 2048; i++)d += asum[i];
                    return(d);}
    void addnum(double);
};
void SUM::addnum(double d)      // add a number into the array
{
size_t i;
    while(1){
//      i = exponent of d
        i = ((size_t)((*(unsigned long long *)&d)>>52))&0x7ff;
        if(i == 0x7ff){         // max exponent, could be overflow
            asum[i] += d;
            return;
        }
        if(asum[i] == 0.){      // if empty slot store d
            asum[i] = d;
            return;
        }
        d += asum[i];           // else add slot to d, clear slot
        asum[i] = 0.;           // and continue until empty slot
    }
}

使用sum类的示例程序:

#include <iostream>
#include <iomanip>
using namespace std;
static SUM sum;
int main()
{
double dsum = 0.;
double d = 1./5.;
unsigned long i;
    for(i = 0; i < 0xffffffffUL; i++){
        sum.addnum(d);
        dsum += d;
    }
    cout << "dsum             = " << setprecision(16) << dsum << endl;
    cout << "sum.getsum()     = " << setprecision(16) << sum.getsum() << endl;
    cout << "0xffffffff * 1/5 = " << setprecision(16) << d * (double)0xffffffffUL << endl;
    return(0);
}

(ARR[0]+ARR[1]...+ARR[N-1])/N更快、更准确,因为使用N可以省略无用的除法,这会减慢计算过程并增加计算错误。

如果你有一堆浮点数,获得平均值的最准确方法如下:

template<class T> T mean(T* arr, size_t N) {
    std::sort(+arr, arr+N, [](T a, T b){return std::abs(a) < std::abs(b);});
    T r = 0;
    for(size_t n = 0; n < N; n++)
        r += arr[n];
    return r / N;
}

要点:

  • 数量级最小的数字首先相加以保留有效数字
  • 只有一个除法,以减少舍入误差

不过,中间金额可能会变得太大。