如果接近零值非常接近零,你就會有一個四捨五入的問題的總和(可以舍入誤差向上或向下),或者如果總結大組數字的任何數值範圍。解決此問題的一個方法是使用求和函數,該求和函數僅添加具有相同指數的數字(直到您調用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
}
}
使用之類
示例程序:
#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);
}
(假設所有數字都是正數)對數字進行排序,從最低到最高,然後從最低到最高添加。 (如果出現負數,則按絕對值排序。)並且不需要將每個元素除以N,只需將總和除以N. – 2014-09-28 18:52:33
另請參閱http://stackoverflow.com/q/13417670/(特別是,Kahan求和) – Nemo 2014-09-28 19:01:02