# 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.