· 6 min read
Math.sumPrecise: accurate floating-point sums in JavaScript
Math.sumPrecise adds numbers without losing precision along the way. How it works, what it can't fix, and where you can use it today.

Math.sumPrecise() is a built-in JavaScript method that adds up an iterable of numbers and returns the exact sum, rounded once to the nearest double at the end. A plain loop or reduce() rounds after every addition, so errors pile up and large values can wipe out small ones. It has been Baseline since April 2026, so current Chrome, Edge, Firefox and Safari all ship it.
If you total measurements, scores, coordinates or anything with mixed magnitudes, this is the method to reach for instead of reduce((a, b) => a + b, 0).
What is Math.sumPrecise?
JavaScript numbers are IEEE 754 doubles: 64-bit floating-point values with about 15 to 17 significant decimal digits. Every + produces a double, so if the true result doesn't fit, it gets rounded. One rounding is harmless. Thousands of them, in a row, are not.
The worst case is catastrophic cancellation, where a large value swallows a small one and a later subtraction removes the large value, leaving nothing behind:
const values = [1e20, 0.1, -1e20];
values.reduce((a, b) => a + b, 0); // 0
Math.sumPrecise(values); // 0.1
The loop computes 1e20 + 0.1 first. That result can't be represented, so it rounds back to 1e20, and the 0.1 is gone for good. Math.sumPrecise treats each input as the exact value it stores, adds them with no intermediate rounding, and only rounds the final total.
The same thing shows up in smaller, everyday sums:
const tenths = Array(10).fill(0.1);
tenths.reduce((a, b) => a + b, 0); // 0.9999999999999999
Math.sumPrecise(tenths); // 1
How does Math.sumPrecise work?
You pass one argument: any iterable of numbers. Arrays work, and so do Sets, Map value iterators, generators and iterator helper chains.
Math.sumPrecise([1, 2, 3]); // 6
Math.sumPrecise(new Set([1, 2, 3])); // 6
Math.sumPrecise(new Map([["a", 1.5], ["b", 2.25]]).values()); // 3.75
function* readings() { yield 1; yield 2; }
Math.sumPrecise(readings()); // 3
It is strict about types, which is a feature rather than an annoyance. Anything that isn't a primitive number throws a TypeError instead of being coerced:
Math.sumPrecise([1, "2"]); // TypeError
Math.sumPrecise([1, 2n]); // TypeError (BigInt)
Math.sumPrecise([1, undefined]); // TypeError
Math.sumPrecise(5); // TypeError: not iterable
Math.sumPrecise({ 0: 1, 1: 2, length: 2 }); // TypeError: array-likes aren't iterable
With reduce, a stray string turns your total into string concatenation and a stray undefined turns it into NaN. Here you get an error at the point where the bad data enters.
A few edge cases are worth knowing:
| Input | Result |
|---|---|
[] (empty) | -0 |
[1, NaN] | NaN |
[Infinity, 1] | Infinity |
[Infinity, -Infinity] | NaN |
[1e308, 1e308, -1e308] | 1e308 |
[1e308, 1e308] | Infinity |
The empty case returns negative zero, because -0 is the identity for floating-point addition: -0 + x is x for every x, including -0. It prints as 0 almost everywhere, but Object.is(result, -0) is true, which matters if you serialize or compare it strictly.
The 1e308 rows show another benefit. The loop overflows to Infinity on the first addition and never recovers. Math.sumPrecise only overflows if the final answer itself is too big for a double.
What doesn't Math.sumPrecise fix?
It gives you the exact sum of the doubles you pass in. It does not turn them into decimals.
Math.sumPrecise([0.1, 0.2]); // 0.30000000000000004
The literal 0.1 is already stored as a binary value slightly above one tenth, and the same goes for 0.2. Their exact sum, rounded to the nearest double, is still 0.30000000000000004. Precise summation removes the error that accumulates while adding; it can't remove the error that existed before you started.
That has a practical consequence for money. Sometimes the precise result even looks less tidy than reduce:
const prices = [19.99, 5.01, 0.1, 0.2, 0.3];
prices.reduce((a, b) => a + b, 0); // 25.6
Math.sumPrecise(prices); // 25.599999999999998
The loop's rounding errors happened to cancel out; the precise sum shows what the stored values actually add up to. For currency, keep using integer minor units (cents) or a decimal library. Math.sumPrecise is for floating-point data where you want the most accurate answer the format can give.
How is it different from Kahan summation?
Kahan summation is a classic technique that keeps a running correction term to recover the low-order bits lost in each addition. It is a big improvement over a plain loop, and some codebases already carry a helper for it. But it is still an approximation, and it fails on the same cancellation patterns shown above:
function kahan(xs) {
let sum = 0, c = 0;
for (const x of xs) {
const y = x - c;
const t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum;
}
kahan([1e20, 0.1, -1e20]); // 0
kahan([1, 1e100, 1, -1e100]); // 0
Math.sumPrecise([1, 1e100, 1, -1e100]); // 2
| Approach | Error | Order-dependent? | Type checks |
|---|---|---|---|
reduce / for loop | Grows with input size | Yes | None |
| Kahan summation | Much smaller, not zero | Yes, in edge cases | None |
Math.sumPrecise | Single final rounding | No | Throws on non-numbers |
Because the result is the correctly rounded exact sum, it is the same no matter what order the values arrive in. That is useful when you sum data from parallel workers, shuffled arrays or unordered sets and want reproducible totals.
Engines implement it with extended-precision algorithms, so it does more work per element than a bare +. In practice the cost is small, but if you sum millions of values in a hot loop, measure it in your target browsers before swapping it in everywhere.
Can you use Math.sumPrecise today?
| Environment | Support |
|---|---|
| Chrome and Edge | 147 and later |
| Firefox | 137 and later |
| Safari | 26.2 and later |
| Node.js | Not in Node.js 26 at the time of writing |
Browser support is solid, but server-side code is a different story: Math.sumPrecise is undefined in current Node.js release lines, including 22, 24 and 26. If the same code runs on both, feature-detect:
const sum = typeof Math.sumPrecise === "function"
? (xs) => Math.sumPrecise(xs)
: (xs) => { let s = 0; for (const x of xs) s += x; return s; };
The fallback has the old accuracy problems, so if you need identical results everywhere, use a polyfill instead. Both core-js and the es-shims project provide one. In TypeScript, the method needs a recent lib setting; until your setup includes it, a one-line declaration on Math will do.
This is part of the same trend as RegExp.escape and the native Uint8Array base64 methods: small built-ins that replace helpers most projects used to copy around, and get the edge cases right.
Key takeaways
Math.sumPrecise(iterable)returns the exact sum of the numbers, rounded once at the end.- It prevents large values from swallowing small ones and avoids intermediate overflow.
- It throws a
TypeErroron strings, BigInts,undefinedand non-iterables instead of silently coercing. - It can't fix values that were imprecise before summing, so
[0.1, 0.2]still gives0.30000000000000004. Use integer cents for money. - It is Baseline since April 2026 in browsers, but not yet in Node.js; feature-detect or polyfill on the server.
FAQ
Why does Math.sumPrecise return -0 for an empty array?
Negative zero is the identity value for floating-point addition, so it is the mathematically neutral starting point. It behaves like 0 in arithmetic and display; only Object.is or division by it reveals the sign.
Is Math.sumPrecise slower than reduce?
It does more work per element, but engines implement it natively and the difference is usually small. Benchmark it if summing is on a hot path.
Should I use Math.sumPrecise for money?
Not as a replacement for proper money handling. It sums the binary approximations of your decimal values exactly, which can still produce results like 25.599999999999998. Store amounts in integer minor units or use a decimal library.