[英]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?
更准确的方法是计算一组数字的平均值ARR[0]/N+ARR[1]/N...+ARR[N-1]/N
或(ARR[0]+ARR[1]...+ARR[N-1])/N
? ( ARR
是数字集合, N
是该集合中数字的计数)
考虑一下我有一组数字,每个数字的范围从0.0
到1.0
(它们是double \\ floating-point数字),并且有成千上万个,甚至数百万个。
我对诸如递归平均的新方法持开放态度(将双胞元平均到数组中,然后再将其平均直到输出一个胞元数组)。
如果接近零的值非常接近于零,则求和(如果将大量数字求和,则取整范围)或任何范围的数字都将出现舍入问题(可能是舍入误差向上或向下)。 解决此问题的一种方法是使用求和函数,该函数仅将具有相同指数的数字相加(直到调用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;
}
要点:
但是,中间金额可能会变得太大。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.