在“简单求和”上避免双重下溢/溢出

时间:2012-10-24 14:22:41

标签: c++ double overflow

我对一个总结问题感到困惑,该问题在失败或失败时失败。

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

但主要的问题是,我似乎并不够聪明。

目前我只是将它们相加并除以大小。 这在大多数情况下都会在under或overflow中失败,给我-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:

所以你们中的一些人是完全正确的。有很多垃圾回滚......

  

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

2 个答案:

答案 0 :(得分:4)

  
      
  • 我有超过8271571个双值,我需要算术平均值。
  •   
  • 从我看到的快速查看值到20到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 :(得分:0)

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

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得到的。

您还可以考虑使用大号库。