0

情况就是这样,我真的不知道将大数相加以计算最后的所有平均值时到底发生了什么。

如果有要编辑的特定错误,请纠正我。

我已经调试过了,我只是在数据中找到了我在以下循环中的正常数据,但似乎变量“somme”给了我一些随机数并给出了一些完全错误的东西。“moyenne”也是如此

别的东西,所有数据都是,或0或正数。索姆有时给出一个负数!

#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%的时间,这是对其他无法解释的行为的解释,但我在这里有点怀疑:如果您发布的代码序列中没有其他内容,并且没有其他线程在运行,则没有指针(至少你操纵)误用。
  • 您未能正确初始化数据。您可能想在最里面的循环中添加一个断言,以确保:
        for (int i = 0; i < 480; ++ i) {
            for (int j = 0; j < 480; ++ j) {
                断言(数据[i][j] >= 0.0 && 数据[i][j] < 200000.0);
                索姆+=数据[i][j];
            }
        }
    

其余的,你的算法不是特别准确。一些快速测试(用 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 == 480data.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 求和算法。)

(我很想补充一点,如果其中任何一个让您感到惊讶,那么无论如何您都不应该使用浮点数。)

于 2013-07-09T11:24:20.083 回答
1

可能发生了数据溢出。溢出改变了符号位,所以它看起来像一个负数。如果您正在处理非常大的数字,请尝试“long double”而不是“double”。

于 2013-07-09T08:55:45.433 回答
0

这也可能是由浮点错误引起的。如果将不同维度的数字(例如 )相加,浮点误差可能会非常大,10e-10 + 10而如果维度相似,则误差会更小。

如果所有数字都很大,您的代码应该可以工作(如果没有溢出)。如果不是,则添加排序后的数字可能会提高准确性。伪代码:

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

原因是,求和的最小数字可能具有更相似的维度,例如下一个更大的数字。这样误差就变小了。

为避免溢出,您可以通过 count(a) 来划分每个 i 而不是划分结果。如果没有发生溢出,这不应该改变准确性。

PS:如果您对数组进行降序排序或反转循环,您可以最大化您的错误!

于 2013-07-09T08:54:02.460 回答