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.
However, there is precision loss in the solution when handling very long integers (equations with small integers worked well). to use the code immediately does not work with "constant integer too long" error. How can I handle such equations in multiple precision without precision loss?
A simple moving average (SMA) is an arithmetic moving average calculated by adding recent closing prices and then dividing that by the number of periods. Technical analysis of stocks and trends is the study of historical market data, including price and volume, to predict future market behavior.
Investors can also use moving averages to identify support or resistance points in order to gauge when prices are likely to change direction. By studying historical trading ranges, support and resistance points are established where the price of a security reversed its upward or downward trend in the past.
The more nearly equal two numbers are, the more precision is lost in the subtraction. Since b is so much larger than a and c, the variable sqrt (d) is very nearly the same as b. The variables b and sqrt (d) agree to 12 decimal places, so about 12 decimal places are lost in the subtraction. How do we compute r2 more accurately?
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;
}
What samgak proposed doesn't actually guarantee good averages if you constantly provide it with evil values.
You can use Neumaier's algorithm to generate accurate results in O(1) time. Something like this:
const double SMA_LENGTH = 5;
Queue values;
double sum = 0.0;
double correction = 0.0;
static void Neumaier(double value, ref double sum, ref double correction)
{
var t = sum + value;
if (Math.Abs(sum) >= Math.Abs(value))
correction += (sum - t) + value;
else
correction += (value - t) + sum;
sum = t;
}
double CalcSMA(double next)
{
Neumaier(-values.pop(), ref sum, ref correction);
Neumaier(next, ref sum, ref correction);
values.push(next);
return (sum + correction) / SMA_LENGTH;
}
If you have huge sequences, you can reset the window every 10^15 or so additions. This is because, with double precision, the algorithm starts losing accuracy after about 10^16 additions.
On the other hand, Neumaier is more complex so it is probably slower.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With