Deterministic decimal arithmetic
Arithmetic that agrees with itself.
Binary floating point cannot represent 0.1, and its results depend on the order operations happen to run in. For systems that move money, that is a defect. Keystone gives the same answer every time, on every platform.
Type a number. See what your machine actually stores.
- you wrote
- 0.1
- f64 stores
- 0.1000000000000000055511151231257827021181583404541015625
- Decimal stores
- 0.1exact
- round to 2dp
- f64 0.10·Decimal 0.10
You wrote 3 characters. Your machine stores 57. The difference is already there before you compute anything, and every operation inherits it.
The defect
Two kinds of wrong, and only one of them is obvious.
The first is representation. A double stores a binary fraction, and one tenth has no finite binary expansion, so 0.1 is already an approximation before you do anything with it. That error is identical on every conforming machine, which is precisely why it survives testing.
The second is divergence, and it is the one that costs money. Because rounding happens at every step, addition stops being associative: regrouping the same operands changes the result, as above. Compilers exploit that freedom with reassociation, fused multiply-add and extended-precision intermediates. On top of that, exp, ln and pow come from a libm that differs across platforms and JavaScript engines. A validator, a laptop and a browser can each compute a liquidation price and disagree.
Keystone stores a 96-bit integer and a decimal scale, so tenths are exact. It computes transcendentals in decimal instead of calling libm. Nothing is left to the platform.
(0.1 + 0.2) + 0.3
0.1 + (0.2 + 0.3)
Same operands. Same machine. Different result. f64 addition is not associative, so grouping changes the answer. Decimal returns 0.6 either way.
…the purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise.Edsger W. Dijkstra · EWD340 · The Humble Programmer · 1972
The stack
Five crates, one arithmetic.
Every crate is #![no_std] and #![forbid(unsafe_code)], dual-licensed MIT and Apache 2.0.
Evidence
Check it yourself.
The same core compiles to Arbitrum Stylus. These three contracts are live on Arbitrum One.
Contract source is not yet published to the explorer. Build from examples/ to reproduce the deployed bytecode.