0

これが状況です。最後にすべての平均を計算するために互いに大きな数を加算すると、実際に何が起こっているのか本当にわかりません。

編集する特定のエラーがある場合は、私を修正してください。

私はデバッグしましたが、次のループで通常のデータをデータで見つけていますが、変数「somme」がいくつかの乱数を与え、何か完全に間違っているようです。「モイエンヌ」も同様

他の何か、すべてのデータは、または 0 または正の数です。Somme は時々負の数を与えます!

#define Nb 230400
std::vector<std::array<double,480>> data(480);

    double somme=0;
    double moyenne=0;
    for (int i=0;i<480;i++)
    {
        for (int j=0;j<480;j++)
            somme=somme+data[i][j];

    }
    moyenne=somme/Nb;
4

3 に答える 3

2

まず、あなたが与えたコードでは、否定的な結果を得る方法はありません (少なくとも、PC と通常の Unix マシンで使用される IEEE 浮動小数点では)。オーバーフローすると、特別な値が得られますInf(ただし、データが指定した範囲内にある場合はオーバーフローできません)。丸め誤差のために結果が間違っている可能性がありますが、下限は 0 のままです。

結果が負であると判断した方法や、入力データが範囲内であることを確認する方法を指定していないため、推測することしかできません。しかし、以下は明確な可能性です:

  • 最適化をオンにしてコンパイルし、デバッガーで値を見ています。デバッガーは、最適化されたコードを見ると、間違った値 (初期化されていないメモリ) を表示することがよくあります。
  • 未定義の動作 (ポインターの問題) が他の場所にあり、ここで見ているメモリが破損しています。99% の確率で、これは他の方法では説明できない動作の説明ですが、私はここでやや疑わしいです: あなたが投稿したコード シーケンスに他に何もなく、他のスレッドが実行されていない場合、ポインターはありません (少なくとも悪用すること。
  • データを正しく初期化できませんでした。念のために、最も内側のループに assert を追加することをお勧めします。
        for ( int i = 0; i < 480; ++ i ) {
            for ( int j = 0; j < 480; ++ j ) {
                assert( data[i][j] >= 0.0 && data[i][j] < 200000.0 );
                somme += data[i][j];
            }
        }
    

それ以外の場合、アルゴリズムは特に正確ではありません。いくつかの簡単なテスト (範囲内のランダムな値でデータ構造を埋める[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 加算アルゴリズムを使用する必要があります。)

(これに驚いた場合は、とにかく浮動小数点を使用するべきではないことを付け加えたいと思います。)

于 2013-07-09T11:24:20.083 に答える
1

データオーバーフローが発生した可能性があります。オーバーフローにより符号ビットが変更されたため、負の数のように見えます。非常に大きな数を扱う場合は、「double」ではなく「long double」を試してください。

于 2013-07-09T08:55:45.433 に答える
0

これは、浮動小数点エラーが原因である可能性もあります。異なる次元 ( など10e-10 + 10) で数値を追加すると、浮動小数点誤差が非常に大きくなる可能性がありますが、次元が類似している場合は誤差が小さくなります。

すべての数値が大きい場合、コードは機能するはずです (オーバーフローがない場合)。そうでない場合は、ソートされた数値を追加すると精度が向上する可能性があります。擬似コード:

array a;
sort(a);
foreach i in a:
    somme += i
somme /= count(a)

その理由は、合計された最小の数値が、次に大きな数値のように、より類似した次元になる可能性があるためです。このように誤差は小さくなります。

オーバーフローを避けるために、結果を除算する代わりに、各 i を count(a) で除算することができます。オーバーフローが発生しない場合、これによって精度が変わることはありません。

PS:配列を降順でソートするか、ループを逆にすると、エラーを最大化できます!

于 2013-07-09T08:54:02.460 に答える