Add the same numbers in a different order and floating point hands you a different answer. Everyone who has shipped numerical code knows this, and there is a well-known cure: compensated summation, Kahan's algorithm, the one in every numerical methods course. Below, your own browser runs Kahan's algorithm against the same sum in five hundred different orders, and watches it disagree with itself. Then it runs the method underneath our compiler, which does not.
Every case above, put through every method, in two hundred random orders each, computed on your machine when this page loaded. A method counts as wrong if it missed the correct answer in any one of those orders. Scoring it as wrong only when it never once got there would reward stumbling onto the right answer by luck in some particular order, which is not a property you can build anything on.
| Method | What it is | Gave more than one answer | Wrong on some ordering |
|---|
Cases we chose prove nothing on their own; we chose them. So generate your own. This draws random vectors with exponents spread across the whole double range, sums each one in several random orders, and records every method that disagreed with itself. It runs until you stop it.
Each trial sums one random dot product in six different orders and compares the results bit for bit.
Reproducibility across one browser is not worth much. These vectors were computed by the reference implementation in Python and published as raw IEEE-754 bit patterns, so nothing is lost to decimal formatting. Your browser just recomputed every one of them with the JavaScript twin. The two implementations share no code.
| Case | Terms | Python said | Your browser said |
|---|
Every IEEE-754 double is exactly an integer times a power of two. That is not an approximation, it is what the format is. So the true value of a dot product is an exact sum of integer products, and integers do not round. Line every product up against the smallest exponent present, add them in a wide integer where nothing is ever discarded, and round the total once at the end.
The result is the infinitely-precise value rounded a single time, which is the uniquely correct double. Because it is unique, there is nothing left for summation order, fused multiply-add, extended precision, or parallel scheduling to change. The usual approach of pinning the order is weaker: it gives you the same answer every time, but a specific wrong one, and a different pinned order gives a different wrong one.
The file doing the arithmetic is floatexact.js, next to this page. It is the
compiler's own fabric/polyglot/floatexact.mjs, byte for byte, with only the
export keywords and its command-line block removed so it loads as a classic
script, because browsers refuse to import modules from a saved local file and this page has
to keep working after you save it. That equivalence is not a promise: the build runs both
files over four thousand random dot products and requires identical bits, and it fails if
either one drifts.
a68efd3970f9108ace9fde32a84b5318621e574529c7df8e74e0105df35af3015c07d1119c1c726946b333678b814b2aa83ba129e63a105f7bba2b830bf1fa9dNo network, no GPU. Save this folder and it keeps working offline.