FixedPointDecimal — high-performance fixed-point decimal arithmetic for Swift
A few weeks ago we open sourced FuzzyMatch, our fuzzy string matching library built with Claude Code. That experiment went well enough that we applied the same approach to another important problem: exact decimal arithmetic that's fast enough for latency-sensitive financial systems.
The problem
Financial systems (or any system that wants to keep user input of decimal numbers correct) need exact decimal arithmetic — Double can't represent 0.1 exactly, causing accumulation errors.
Foundation.Decimal solves exactness but carries inherent overhead: it's a 20-byte variable-precision type with multi-word mantissa arithmetic. We contributed a fix to swift-foundation that removed heap allocations from Decimal's critical path (merged, ~3-5x improvement, should land with Swift 6.4), but even with that fix the architectural gap remains — variable-precision arithmetic is fundamentally more work than operating on a single machine integer.
For both throughput and latency-sensitive code paths processing tens- or hundred- of thousands of prices per tick, that overhead matters.
What it is
FixedPointDecimal is an @frozen struct backed by Int64, storing values with exactly 8 fractional decimal digits (value × 10⁸). All arithmetic compiles to native integer instructions with zero heap allocations.
let price: FixedPointDecimal = 123.45
let quantity: FixedPointDecimal = 1000
let notional = price * quantity // 123450
Eight fractional digits cover all practical financial instruments: cents (2), mils (3), basis points (4), FX pips (5), and cryptocurrency satoshis (8). The range (±92 billion) is sufficient for individual prices and quantities.
We have tried to follow standard library conventions and Decimal conventions as far as possible to make it easy to drop this in as a replacement if desired.
Performance
On Apple Silicon (M4 Max), compared to Foundation.Decimal:
| Operation | FixedPointDecimal | Foundation.Decimal | Speedup |
|---|---|---|---|
| Addition | 0.67 ns | 240 ns | 359x |
| Comparison | 0.33 ns | 300 ns | 901x |
| Hash | 5 ns | 261 ns | 48x |
| Multiplication | 8 ns | 607 ns | 79x |
| Division | 8 ns | 1,285 ns | 168x |
init(Double) |
1.4 ns | 2,319 ns | 1,622x |
rounded(scale:) |
2 ns | 705 ns | 349x |
| JSON encode | 320 ns | 1,215 ns | 3.8x |
| JSON decode | 457 ns | 831 ns | 1.8x |
Zero heap allocations across all operations. At 8 bytes vs Decimal's 20, arrays of prices use 40% of the memory — relevant when working with large datasets with millions of entries - both in memory, on disk or over the network.
Design choices
-
Banker's rounding everywhere — all entry points (string parsing, Double conversion, Decimal conversion, arithmetic) use round-half-to-even. The same input always produces the same stored value regardless of construction path.
-
Safe by default — trapping arithmetic matching Swift
Int. Wrapping (&+,&-,&*) and overflow-reporting variants available. -
NaN as sentinel —
Int64.minas the NaN value, with trapping semantics. No optional wrapper overhead. -
@frozen— enables cross-module inlining and optimalContiguousArraylayout.
Protocol conformances
Sendable, BitwiseCopyable, AtomicRepresentable, Equatable, Hashable, Comparable, Numeric, SignedNumeric, Strideable, Codable, LosslessStringConvertible, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral SwiftUI: VectorArithmetic (animations), Plottable (Charts), FormatStyle, ParseableFormatStyle with full Decimal.FormatStyle forwarding for locale-aware currency/number/percent formatting.
Quality
549 tests across 15 suites, including property-based parity testing against Foundation.Decimal (10k iterations), exhaustive float-literal precision verification (1.1M+ values), and a libFuzzer fuzz harness with AddressSanitizer.
Acknowledgments
Like FuzzyMatch, this was built with Claude Code Opus 4.6 with careful guidance. The approach works well for self-contained, heavily testable components: we steer architecture and review every change, Claude writes the code, tests, benchmarks, and documentation. The overall implementation achieved in just two days — 15 source files, 13 test suites, benchmark suite, fuzz harness, DocC documentation — would have taken considerably longer manually.
Getting started
If you need exact decimal arithmetic within the supported range with excellent performance — give it a try.
Joakim