Search code examples
algorithmfloating-point-precisionmoving-average

Avoiding Possible Precision Loss with a Simple Moving Average


Suppose we had a basic moving average function that was keeping track of the sum. For example:

Queue values;
double sum;

double CalcSMA(double next) {
    values.push(next);
    sum -= values.pop();
    sum += next;
    return sum / SMA_LENGTH;
}

One example of how this could break down would be if our window was 5 wide that we fed it something like: 2, 2, 2, 2, 2, 2, 2, 1E100, 2, 2, 2, 2, 2, 2, 2, 2. The output would then be 2, 2, 2, 2E99, 2E99, 2E99, 2E99, 2E99, 0, 0, 0.

Even if the sum isn't quite that dramatically off, there still could be quite reasonable instances where a small loss in precision could make the sum artificially increase by a tiny amount. Over a long period of time, this could add up and become an issue.

Does anyone have any ideas of how to work around the loss in precision?

EDIT: note that this function is designed to work O(1). That is necessary. So, recalculating each time won't work: the window is too large.


Solution

  • You could recalculate a fresh sum over every SMA_LENGTH values to stop the errors accumulating:

    Queue values;
    double sum = 0.0;
    double freshsum = 0.0;
    int count = 0;
    
    double CalcSMA(double next) {
        values.push(next);
        sum -= values.pop();
        sum += next;
        freshsum += next;
        if(++count == SMA_LENGTH)
        {
            sum = freshsum;
            freshsum = 0.0;
            count = 0;
        } 
        return sum / SMA_LENGTH;
    }