【问题标题】:Avoiding Under-/ Overflow with double on "simple Summation"在“简单求和”上使用双精度避免欠/溢出
【发布时间】:2012-10-24 14:22:40
【问题描述】:

我正在努力解决一个因不足或溢出而失败的求和问题。

我有超过 8271571 个双精度值,我需要算术平均值。

但主要问题是,我似乎不够聪明,无法做到这一点。

目前我只是将它们相加并除以大小。 这在大多数情况下都会失败或溢出,给我 -1.#INF 或 1.#INF。

for(size_t j = 0; j < 12; j++)
{
    double a = 0.0;

    for(size_t i=0; i < Features->size(); i++)
    {
        a += Features->at(i)->at(j);
    }
    meanVector[j] = a / Features->size();
}

但是,不能说它只是正值或负值,所以我不能设置要签名的数据类型。

当我将它们相加时,我也尝试在求和中使用除法常数或除以大小,但这也无济于事。

值的范围,从我快速浏览的结果来看,从 -20 到 +30,但不能肯定地说。

所以也许任何人都可以给我一个关于如何做数学或使用解决方法的提示。这一定可以,但我只是缺乏想法。

编辑:

大小永远不会为0,在划分之前进行检查。 此外,没有任何值以任何方式无效。在提取它们时,我已经检查了#IND 和 NaN。

如果我已经在总和上除,我想这也不是正确的结果?

a+= Features->at(i)->at(j) / Features->size()

结果为 -3.7964983860343639e+305

但对于每次迭代。这不可能是正确的,看起来像一个边界

编辑 2:

所以你们中的一些人是完全正确的。有很多垃圾sh * t发生..

0:大小:8327571,最小值:-2.24712e+307,最大值:3362.12 1:大小:8327571, 最小值:-2.24712e+307,最大值:142181 2:大小:8327571,最小值:-2.24712e+307, 最大值:59537.8 3:大小:8327571,最小值:-2.24712e+307,最大值:236815 4: 大小:8327571,最小值:-2.24712e+307,最大值:353488 5:大小:8327571,最小值: -2.24712e+307,最大值:139960 6:大小:8327571,最小值:0,最大值:0 7:大小:8327571,最小值:0,最大值:0 8:大小:8327571,最小值:0,最大值:0 9:尺寸: 8327571,最小:0,最大:0 10:大小:8327571,最小:0,最大:0 11:大小: 8327571,最小值:0,最大值:0

【问题讨论】:

  • 您可以求和number[i]/n - 但它可能会增加数字错误...
  • 如果所有Features-&gt;size() 都不是0,您确定吗? (提示:任何浮点数除以 0 将得到无穷大 - 如果数字是非负数 - 1.#INF,如果是负数 - -1.#INF
  • 其中一个被求和的值也可能是 +/-INF - 您应该添加一个断言来检查这一点。
  • 我根据您的要求编辑了一些信息。
  • 看起来即使max of size_t 的值从-20 到30 也不太可能达到double 的范围的末尾。我认为你的一个假设是不正确的。

标签: c++ double overflow


【解决方案1】:
  • 我有超过 8271571 个双精度值,我需要计算平均值。
  • 值的范围从我快速查看的范围,从 -20 到 +30,但不能肯定地说。
  • 大小永远不为0,在分区前做检查。

这不加起来。总和应该很容易适合double。数据一定有问题。您可以像这样快速检查您的值:

for (size_t j = 0; j < 12; ++j)
{
    std::vector<double> values;

    values.reserve(Features->size());
    for (size_t i = 0; i < Features->size(); ++i)
    {
        values.push_back(Features->at(i)->at(j));
    }

    // Find extreme values, including infinity
    std::cout << j << ": " 
              << "size: " << values.size() 
              << ", min: " << *std::min_element(values.begin(), values.end())
              << ", max: " << *std::max_element(values.begin(), values.end())
              << std::endl;

    // Find NaNs
    for (size_t i = 0; i < Features->size(); ++i)
    {
        // Choose one of the following ifs

        // For C++11 (isnan is a standard thing now)
        if (std::isnan(Features->at(i)->at(j))

        // Or for Visual Studio
        if (_isnan(Features->at(i)->at(j))

        // Or for GCC prior to C++11
        if (__builtin_isnan(Features->at(i)->at(j))

        {
            std::cout << "NaN at [" << i << ", " << j << "]" << std::endl;
        }
    }
}

您应该能够快速发现输入是否有任何异常。

【讨论】:

  • +1。 这不加起来 - 字面上和比喻上。有些数据有些古怪。我怀疑某些值尚未初始化。这些数字大概代表了一些物理的东西。不管是米、公斤、#人,什么的,10^300都是垃圾值。
  • +1 你们完全正确! 0:大小:8327571,最小值:-2.24712e+307,最大值:3362.12 1:大小:8327571,最小值:-2.24712e+307,最大值:142181 2:大小:8327571,最小值:-2.24712e+307,最大值: 59537.8 3:尺寸:8327571,最小:-2.24712e+307,最大:236815 4:尺寸:8327571,最小:-2.24712e+307,最大:353488 5:尺寸:8327571,最小:-2.24712e+307,最大: 139960 6: 大小: 8327571, min: 0, max: 0 7: size: 8327571, min: 0, max: 0 8: size: 8327571, min: 0, max: 0 9: size: 8327571, min: 0 , max: 0 10: size: 8327571, min: 0, max: 0 11: size: 8327571, min: 0, max: 0 真是一堆垃圾,真丢脸!
  • 嗨,我现在已经“解决”了获取垃圾数据的问题。读取数据时出现了一个小错误。无论如何我发现了一个奇怪的行为。使用循环检查元素可以正常工作,并为我提供漂亮而流畅的数据。没有错误或垃圾值。但是如果我稍后总结它们,我会在向量 -1.#IND .. 中找到一个值,但是在将它推入循环中的值时,它不是。使用已知索引直接访问它我可以再次看到#IND.. 疯狂?
  • @Stefan,您的数据中有 NaN。 min/max_element 无法检测到它。我编辑了代码来专门寻找那些。如果您不知道 NaN 是什么,可以在此处阅读 (en.wikipedia.org/wiki/NaN)。基本上它不是数字,就像sqrt(-1) 的结果一样。
  • @Stefan, value != value 并不总是有效。我认为编译器可以优化它。 isnan 是你的朋友。 min_element 无法检测到它们,因为它们既不是&lt; 也不是&gt;,而不是任何其他元素。在某些特殊情况下,您可以通过min_element 将其返回,但一般情况下不会。
【解决方案2】:

您可以使用在线算法计算平均值,这意味着您不必在除法之前添加所有值。这里:

template< typename NumberType >
class ProgressiveMean{
    NumberType  m_Mean;
    NumberType  m_MeanKMinus1;
    long        m_K;
public:
    ProgressiveMean();
    void Seed( NumberType seed );
    void AddValue( NumberType newVal );
    NumberType getMean() const;
};

template< typename NumberType >
ProgressiveMean<NumberType>::ProgressiveMean():
    m_Mean( 0 ),
    m_MeanKMinus1( 0 ),
    m_K( 0 ){
}

template< typename NumberType >
void ProgressiveMean<NumberType>::Seed( NumberType seed ){
    m_MeanKMinus1 = seed
    m_K = 2;  //Start from K = 1, so next one is 2
}

template< typename NumberType >
void ProgressiveMean<NumberType>::AddValue( NumberType newVal ){
    m_Mean = m_MeanKMinus1 + (newVal - m_MeanKMinus1) / m_K;
    m_MeanKMinus1 = m_Mean;
    m_K++;
}

template< typename NumberType >
NumberType ProgressiveMean<NumberType>::getMean() const{
    return m_Mean;
}

要使用它,使用初始值调用Seed,其余部分循环调用AddValue,完成后调用getMean

这个想法来自 Knuth,我是从 here 那里得到的。

您也可以考虑使用大数字库。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    • 2023-03-27
    • 2015-06-19
    相关资源
    最近更新 更多