Functional Weave
Code in TypeScript

stats.weighted-average@1.0.0

README.md

1,440 bytes · view raw

# stats.weighted-average

Sum of value x weight, divided by the sum of the weights: the average price
paid over several purchases, a grade point average weighted by credits, a
stock's average cost.

Everything up to the last step is integer arithmetic, so the only rounding is
the one the caller asked for. The exact quotient sum(v x w) x 10^decimals /
sum(w) is rounded once by `math.round-div` in the given mode (half-up,
half-even, down or up), and the resulting integer is divided by 10^decimals.
That last division returns the nearest binary64 to a decimal with at most
fifteen significant digits, which is the same double in every language and
prints back as exactly that decimal. There is no float accumulation to drift.

Values and weights are integers on purpose. Take money in pence and weights in
whole units (or scale fractional weights, 0.25 as 25); then the average is
exact before it is rounded.

Weights of zero are allowed and simply contribute nothing. Negative weights
are an error, and so is a list where every weight is zero: there is no
average of nothing.

The weighted sum, scaled by 10^decimals, must stay within 2^53 - 1 (the range
integers share across TypeScript, Python and Rust). Beyond that the capability
refuses rather than rounding silently; lower `decimals` or rescale the values.

A naive "average of the averages" (15 for one item at 10 and three at 20) is
the classic mistake here; the answer is 17.5.