まず、あなたが与えたコードでは、否定的な結果を得る方法はありません (少なくとも、PC と通常の Unix マシンで使用される IEEE 浮動小数点では)。オーバーフローすると、特別な値が得られますInf
(ただし、データが指定した範囲内にある場合はオーバーフローできません)。丸め誤差のために結果が間違っている可能性がありますが、下限は 0 のままです。
結果が負であると判断した方法や、入力データが範囲内であることを確認する方法を指定していないため、推測することしかできません。しかし、以下は明確な可能性です:
それ以外の場合、アルゴリズムは特に正確ではありません。いくつかの簡単なテスト (範囲内のランダムな値でデータ構造を埋める[0...2e5)
) では、最終結果の精度が 15 桁未満であることが示されます。(もちろん、これは許容できるかもしれません。取得するほとんどの物理データの精度はとにかく 3 桁または 4 桁を超えることはなく、6 桁を超えて表示されない場合もあります。その場合は...)
精度の問題は実際には興味深いものであり、これらがいかに難しいかを示しています。テストには 3 つの関数を使用しました。
// 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
.)
このコードは、オプション /O2 を指定して VC11 を使用してコンパイルされました。興味深いのは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 加算アルゴリズムを使用する必要があります。)
(これに驚いた場合は、とにかく浮動小数点を使用するべきではないことを付け加えたいと思います。)