首先,使用您给出的代码,您不可能得到负面结果(至少对于 PC 和通常的 Unix 机器上使用的 IEEE 浮点);如果溢出,您将获得特殊值Inf
(但如果数据在您指定的范围内,则不会溢出)。由于舍入误差,结果可能是错误的,但它们的下限仍为 0。
您没有指定如何确定结果是否定的,也没有指定如何确保输入数据在范围内,所以我只能推测;但以下是不同的可能性:
其余的,你的算法不是特别准确。一些快速测试(用 range 中的随机值填充数据结构[0...2e5)
)显示最终结果的准确度低于 15 位。(当然,这可能是可以接受的。无论如何,您获取的大多数物理数据的精度都不会超过 3 或 4 位,而且您显示的可能不会超过 6 位。在这种情况下......)
准确性问题实际上很奇怪,并显示了这些事情有多么棘手。我在测试中使用了三个函数:
// Basically what you did...
double
av1( std::vector<std::array<double, cols>> const& data )
{
double somme = 0.0;
for ( int i = 0; i != data.size(); ++ i ) {
for ( int j = 0; j != cols; ++j ) {
somme += data[i][j];
}
}
return somme / (data.size() * cols);
}
// The natural way of writing it in C++11...
double
av2( std::vector<std::array<double, cols>> const& data )
{
return std::accumulate(
data.begin(),
data.end(),
0.0,
[]( double a, std::array<double, cols> const& b ) {
return a + std::accumulate( b.begin(), b.end(), 0.0 );
} ) / (data.size() * cols);
}
// Using the Kahan summation algorithm...
double
av3( std::vector<std::array<double, cols>> const& data )
{
double somme = 0.0;
double c = 0.0;
for ( int i = 0; i != data.size(); ++ i ) {
for ( int j = 0; j != cols; ++j ) {
double y = data[i][j] - c;
double t = somme + y;
c = (t - somme) - y;
somme = t;
}
}
return somme / (data.size() * cols);
}
(在所有测试中,cols == 480
和data.size() == 480
。)
该代码是使用 VC11 编译的,带有选项 /O2。有趣的是,它av2
在系统上比您的代码更准确,通常到第 17 位(内部表示中的 2 或 3 位),而
av1
在第 15 位(8 或 9 位)中通常会偏离多达 2 或 3 位在内部表示中)。这样做的原因是您的代码系统地收集到xmm1
所有
480*480
值中,其中 asav2
分别收集每一行;这导致数量级差异较大的添加较少。(随着av1
接近数据的末尾,somme
接近2.3e10
,它比任何数据元素都大得多。)使用类似的东西:
double
moyenne( std::vector<std::array<double, cols>> const& data )
{
double outerSum = 0.0;
for ( int i = 0; i != data.size(); ++ i ) {
double innerSum = 0.0;
for ( int j = 0; j != cols; ++ j ) {
innerSum += data[i][j];
}
outerSum += innerSum;
}
return outerSum / (data.size() * cols);
}
应该给出等效于 的结果av2
。(但如果你需要准确性,你真的应该使用 Kahan 求和算法。)
(我很想补充一点,如果其中任何一个让您感到惊讶,那么无论如何您都不应该使用浮点数。)