Decimal::from_str("100000000000.000000000000000001") returns Ok. The value inside is 100000000000.00000000000000000: rust_decimal dropped the last wei to fit its 96-bit mantissa and raised no error. Call Decimal::from_str_exact on the same string and you get Err(Underflow).
The pages ranking for "rust_decimal vs bigdecimal" mostly stop at "fixed-size and fast versus arbitrary precision", which is true and not enough to choose with. So I built a scratch crate against rust_decimal 1.43.0 and bigdecimal 0.4.11 on Rust 1.98 and ran the cases payment code hits: token amounts, rounding, JSON, Postgres and overflow. Unless I say otherwise, every output below is pasted from that run.
How Should You Represent Money in Rust?
There are five realistic options, and one of them is wrong.
| Type | Exact decimals? | Size | Heap? | Range | Use it for |
|---|---|---|---|---|---|
f64 |
No | 8 bytes | No | 15 to 17 significant digits | Nothing involving amounts |
i64 minor units |
Yes | 8 bytes | No | ±9.22 × 10¹⁸ minor units | Fiat balances and postings |
i128 / u128 minor units |
Yes | 16 bytes | No | up to 3.4 × 10³⁸ | Token balances, TigerBeetle-style ledgers |
rust_decimal::Decimal |
Yes, to 28 digits | 16 bytes | No | ±79,228,162,514,264,337,593,543,950,335 | Rates, fees, tax, FX, API amounts |
bigdecimal::BigDecimal |
Yes, unbounded | 40 bytes + digits | Yes | Memory | Boundaries where inputs can exceed 28 digits |
f64 fails the first test anyone runs. Adding 0.1 ten times gives 0.9999999999999999, and 0.1 + 0.2 == 0.3 is false. Both decimal crates return true.
Integer minor units are what Stripe and Adyen put on the wire, and what I used for the Postgres schema in the TigerBeetle vs Postgres ledger guide. An i64 holds 92,233,720,368,547,758.07 when the unit is pence. No fiat ledger gets close. The same i64 holds 9.223372036854775807 ETH when the unit is wei. That gap between fiat and 18-decimal tokens shapes most of what follows.
rust_decimal vs bigdecimal vs fastnum in 2026: Which Crate?
| rust_decimal 1.43.0 | bigdecimal 0.4.11 | fastnum 0.7.5 | |
|---|---|---|---|
| Representation | 96-bit integer, scale 0 to 28, sign bit |
BigInt plus i64 scale |
Fixed N-bit coefficient plus 64-bit control block |
| Significant digits | 28 (29 in some cases) | Unbounded | 19 (D64), 38 (D128), 77 (D256), 154 (D512) |
Copy, no heap |
Yes | No (Clone, heap digits) |
Yes |
| Default rounding | Half-even | Half-even for round, truncation for with_scale
|
Half-up |
| NaN and Infinity | No | No | Yes |
| serde default | String out; string or number in | String out; string or number in | String out; string only in |
| Postgres support | sqlx, Diesel, tokio-postgres | sqlx, Diesel | sqlx, Diesel, tokio-postgres |
| Total downloads (3 Nov 2026) | 169.1M | 139.5M | 10.6M |
| Latest release | 2 Sep 2026 | 28 Sep 2026 | 11 Jun 2026, pre-1.0 |
Read the full article on tomcn.uk →
About the Author
I'm Tom Wang, an AI Developer & Fintech Developer — building AI agents, crypto payment infrastructure, and cross-border payout systems with Rust, Go, and TypeScript. Based in London, UK.
Currently open to new opportunities in fintech, crypto payments, and AI agent engineering.
Top comments (0)