# math.round-float
Rounds a floating-point number to 0-12 decimal places, half away from zero,
and returns the same double in TypeScript, Python and Rust. It is the one
rounding step for everything in the registry that works in floats (chart
geometry, colours, statistics that choose to use it), so "rounded to 2 decimal
places" means the same thing everywhere.
## The rule
The result is the multiple of 10^-decimals nearest to the **exact value of
the double you passed**, with an exact tie going away from zero, and then the
nearest double to that decimal. So:
- `0.5` → `1`, `2.5` → `3`, `-2.5` → `-3`, `0.125` to 2 places → `0.13`.
Those are exact binary values and true ties. Python's `round()` is
half-even (2 and 0.12) and JavaScript's `Math.round` rounds -2.5 to -2.
- `1.005` to 2 places → `1`, `2.675` → `2.67`, `1.45` to 1 place → `1.4`.
None of those literals can be stored exactly; each is stored slightly below
the tie (2.675 is 2.67499999999999982236...), so it rounds down. This
matches JavaScript's `toFixed`. It does not match the shortcut
`Math.round(x * 100) / 100`, which says 2.68, because `2.675 * 100` itself
rounds to exactly 267.5 before `Math.round` sees it.
- `8.345` → `8.35` and `0.0005` to 3 places → `0.001`: stored slightly above.
- `0.49999999999999994` → `0`. `floor(x + 0.5)` says 1, because the addition
rounds up to exactly 1.
- The result is never `-0`: `-0.001` to 2 places is `0`, so it prints as `0`.
When `|value| x 10^decimals` is 2^52 or more, the double has no digits left at
that precision (the gap between neighbouring doubles is already that coarse),
and it is returned unchanged. That is at most one unit in the last place from
the correctly rounded decimal.
## Why all three languages agree
The scaled value `|value| x 10^decimals` is computed once, its rounding error
is recovered exactly with Dekker's TwoProduct (Veltkamp splitting; only `*`
and `-`, so no fused multiply-add, which JavaScript lacks), and the tie is
decided on their sum. Every step is an IEEE 754 operation the standard defines
exactly, in the same order in each language; there is no library call whose
last bit could differ.
`stats.*` capabilities published before this one round the scaled product
instead (`floor` of `|value| x 10^decimals`, then compare with a half), which
agrees everywhere except on literals like 2.675 whose product lands exactly on
a tie. New float capabilities should require this one rather than carry their
own rounding.
Sources: IEEE 754-2019 (correctly rounded +, -, *, /); T. J. Dekker, "A
floating-point technique for extending the available precision", Numerische
Mathematik 18 (1971) 224-242.