# stats.moving-average
Smooths a series. Both kinds return one value for every position that has a
full window behind it, so the output has `len(values) - window + 1` entries
and the i-th output lines up with input `i + window - 1`. A series shorter than
the window returns an empty list rather than an error: a chart with too few
points yet should draw nothing, not fail.
- `simple`: the plain mean of the last `window` values.
- `exponential`: alpha = 2 / (window + 1), the usual span convention. It is
seeded with the simple average of the first window (so its first output
equals the simple one), then each step is ema + alpha x (value - ema). Seeding
with the first value instead, as some libraries do, gives different numbers
for the whole series; the vectors pin this choice down.
**Precision.** Every output is rounded to `decimals` places (0 to 12) by
`math.round-float`: half away from zero, decided on the exact value of the
double, -0 returned as 0. The running exponential average is carried
unrounded; only what is returned is rounded, so the rounding does not compound.
"The exact value of the double" is literal: 1.005 is stored as
1.00499999999999989..., so it rounds to 1.00 at two places, and the mean of
2.67 and 2.68 is the double 2.67499999999999982..., so it rounds to 2.67.
Callers who need decimal-exact averages of money should average integer minor
units with `stats.weighted-average` instead.
**Why the three languages agree to the bit.** Each window is summed afresh,
left to right (not by a running add-and-subtract, which accumulates error
differently depending on history), and only IEEE-754 +, -, x, / and floor are
used, in the same order in every language. Each of those operations is
correctly rounded by the standard, so TypeScript, Python and Rust hold the
same double before rounding; `math.round-float` then rounds that double the
same way in all three.
## Changes in 2.0.0
2.0.0 rounds on the exact value of the double, so an average of 2.67 and 2.68
(the double 2.67499999999999982...) now gives 2.67. 1.x rounded the scaled
product instead (floor of |x| x 10^decimals, compared with a half), and
2.675 x 100 is exactly 267.5 in floating point, so 1.x said 2.68. The private
rounding helper is gone: this version requires `math.round-float ^1.0.0` and
rounds with it, so every float capability in the registry rounds alike.
Only outputs whose double lies just below (or just above) a tie that the
scaled product lands exactly on change; everything else is the same double.
None of the 1.x vectors changed answers (each was recomputed from the exact
value of its double). New vectors pin the difference: the mean of 2.67 and
2.68 gives 2.67 (1.x 2.68), of -2.67 and -2.68 gives -2.67 (1.x -2.68), an
exponential output of 2.675 gives 2.67 (1.x 2.68), and 1.45 at one place
gives 1.4 (1.x 1.5); 8.345, stored just above its tie, still gives 8.35.
`decimals` is still 0 to 12, and still refused up front with the same message
("decimals must be a whole number from 0 to 12"), so a series too short for
any output refuses it as before.