Search code examples
c++boostboost-accumulators

boost::accumulators::rolling_mean returning incorrect mean value


Environment: VS 2013, Boost 1.58

I've written something that presents a more friendly interface to Boost's accumulator, which can be used to project a sum over a window, and calculate the actual rolling mean over the window. During a push to get to VS 2013 as our main compiler, one of the unit tests for this class started failing. Peeling off the layers, I've narrowed it down to this minimal example:

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>

namespace ba = boost::accumulators;
namespace bt = ba::tag;
typedef ba::accumulator_set < uint32_t, ba::stats <bt::rolling_mean > > MeanAccumulator;

int main() {

    MeanAccumulator acc(bt::rolling_window::window_size = 5u);

    for (uint32_t i : { 3, 2, 1, 0 }) {
        acc(i);
        std::cout << i << " actualMean: " << std::fixed << boost::accumulators::rolling_mean(acc) << "\n";
    }
}

On the final pass of the loop, I don't get the expected mean value (1.5), but instead get a crazy high value (1431655766.333333).

This code executes correctly in VS 2008 with Boost 1.49 (with C++11 vector initialization replaced, obviously), but fails in VS 2012 and VS 2013 with Boost 1.58. I'm at a loss to explain this failure and thus can't fix it.

Other interesting points:

  • Manually inspecting the memory values inside the accumulator reveals that the correct data IS contained in its circular buffer.
  • If the data put into the accumulator is ordered in increasing value, the rolling_mean will be correct.
  • Once the window is full, if any new element is smaller than the number it is knocking out of the window, the rolling_mean will be incorrect. If it is equal or greater, the rolling_mean will be correct.

It seems to be a Boost bug, but wanted to verify that I'm not doing something stupid before reporting the bug or attempting to build Boost 1.59. Thanks in advance!

EDIT: Thanks for the responses, as this does appear to be a Boost bug. The associated Boost ticket is here.. It's an issue related to unsigned integers with accumulators. Specifically, if a value added to the accumulator after the window is full is strictly less than all values in the window already, the rolling_mean call will return an invalid result.

There is a workaround, which is to not use unsigned integers with accumulators. This solves my problem, so thanks for the help!


Solution

  • There's obviously a bug lurking there, possibly in the compiler, but more likely in the library, as I've been able to reproduce this on GCC:

    Live On Coliru

    #include <boost/accumulators/accumulators.hpp>
    #include <boost/accumulators/statistics.hpp>
    #include <boost/accumulators/statistics/rolling_mean.hpp>
    
    namespace ba = boost::accumulators;
    namespace bt = ba::tag;
    typedef ba::accumulator_set < uint32_t, ba::stats <bt::rolling_mean > > MeanAccumulator;
    
    int main() {
    
        MeanAccumulator acc(bt::rolling_window::window_size = 5u);
    
        for (uint32_t i : { 252, 189, 248, 154, 620, 885, 939, 196 }) {
            acc(i);
            std::cout << i << " actualMean: " << std::fixed << boost::accumulators::rolling_mean(acc) << "\n";
        }
    }
    

    Prints

    g++-5.2 -std=c++1y -O2 -Wall -pedantic main.cpp && ./a.out
    252 actualMean: 252.000000
    189 actualMean: 220.500000
    248 actualMean: 229.666667
    154 actualMean: 210.750000
    620 actualMean: 292.600000
    885 actualMean: 419.200000
    939 actualMean: 569.200000
    196 actualMean: 858994018.000000
    

    Now the problem appears to be related to the choice of unsigned sample type`: changing it to signed removes the symptom: Live On Coliru


    In short: I'd report this at the boost mailing list or Trac: https://svn.boost.org/trac/boost/