Open-source project
paupino/rust-decimal avatar
paupino/rust-decimal

rust_decimal: exact decimal arithmetic, and a 2.0 branch that breaks on purpose

Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations.

1,343 stars240 forksRustMIT

At a glance

What is it?
rust_decimal is a pure Rust implementation of decimal numbers for financial and fixed precision work, storing a 96 bit integer with a scaling factor and a sign bit, which preserves trailing zeros and avoids round-off. The main branch is now an unstable 2.0 rewrite, while the 1.x line lives on its own branch and still takes patches, so the version you pick is a decision rather than a detail.
Who is it for?
Adopt rust_decimal if you need exact decimal arithmetic in Rust, particularly for money, where binary floating point is the wrong type, and pick the 1.x line if you need a stable, patchable dependency today, since the README says the main branch is unstable and that breaking changes are expected there.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The representation, and why trailing zeros survive

The README states the representation plainly, and it is the design decision that explains the library's behaviour. The binary representation consists of a 96 bit integer number, a scaling factor used to specify the decimal fraction, and a 1 bit sign. That is a fixed precision decimal in the classic sense, with the mantissa spread across three 32 bit words and the scale selecting the position of the decimal point, which is why a value like pi is stored exactly as the digits you give it. Because the value is stored as an integer and a scale rather than as a mantissa and an exponent, there is no normalisation step that would throw information away, so trailing zeros are preserved and may be exposed when the number is printed in string form. The README tells you how to deal with that, with the `normalize` and `round_dp` functions, and it is worth noticing which of the two you want. `round_dp` changes the value by rounding to a number of decimal places, while `normalize` is what you reach for when you want a value printed without the padding you do not care about.

Two branches, and an explicit list of what is about to break

The most useful section in the README is the one headed Please read before contributing, and it changes how you should think about the crate. The main branch is now considered unstable, and the stable 1.x version has been branched and exists in the `v1` branch. Patch requests and bug fixes are still accepted against `v1`, but they will need to continue being backwards compatible and align with the support model. The main branch also accepts contributions, and any contributions to it are permitted to be breaking, because the work is towards an improved Decimal API. The README then lists what is expected to break over the coming months. Removal of deprecated APIs. Removing the use of panics within the code without explicit opt in. Clean up of error types for `const` and `no-std` support. And module system experimentation. That last one is the big structural risk, since a reorganisation of the public API breaks every import path, and the second is the one that changes your error handling, since a crate that panics by default and one that returns errors are different designs for the same operation.

Creating a decimal, from a macro or from parts

There are two ways to get a value, and the macro is the one the README recommends as the easiest and most efficient. With the `macros` feature enabled, `dec!` parses the input at compile time and converts it to an optimised Decimal representation, and an invalid input becomes a compile time error rather than a runtime one.

rust
use rust_decimal::prelude::*;

assert_eq!(dec!(1.23), Decimal::new(123, 2));

Any Rust number format is supported, including scientific notation and alternate bases. The constructor family covers the rest. `Decimal::new` takes an integer and a scale, `from_i128_with_scale` handles values that do not fit in 64 bits, `from_str` and `from_str_radix` parse text, `from_scientific_exact` parses scientific notation, and `from_parts` takes the low, middle and high 32 bit words with a sign and a scale, which is how you reconstruct a value such as pi from its exact digits. The prelude brings the trait based functions in, including `FromStr`. Once you have a value, it behaves like a number, with the arithmetic done in decimal and rounding applied explicitly, as in the README's example where a tax percentage is multiplied and then rounded to two decimal places before being added.

Feature flags as the compatibility surface

The feature list is long and it is where the real decisions live. `alloc` enables features that need heap allocation without requiring `std`, which suits `no_std` environments that have an allocator, and it currently gates the scientific notation Display implementations and is enabled by default and implied by `std`. `std` exists for the opposite case, and `align16` forces the alignment of Decimal to 16 bytes, identical to `u128` and `i128` on x86 platforms. `c-repr` forces a `repr(C)` layout, and `bytemuck` derives `Pod` and `Zeroable` for Decimal, which also activates `c-repr` because `Pod` requires it. There are serialisation features in several flavours, with `serde-float`, `serde-str`, `serde-arbitrary-precision` and the `serde-with-` variants that keep the decimal exact while carrying a float alongside it, and `maths` adds functions such as `pow`, `ln` and `exp`. `wasm`, `ndarray`, `rkyv`, `borsh` and `rust-fuzz` cover the other ecosystems you might need. The manifest declares `no-std::no-alloc` as a category, and the docs.rs metadata builds with all features and for a Linux and a wasm target, so the documentation you read is the widest configuration the crate supports.

Databases, and the serde choice that decides your precision story

The database features are the ones that matter in the accounting systems this crate is aimed at, and they are specific. `db-postgres` enables a PostgreSQL communication module that reads and writes the Decimal type by transparently serialising and deserialising into the `NUMERIC` data type, and `db-tokio-postgres` does the same for the async client. There are diesel variants for PostgreSQL and for MySQL. The manifest pulls in `postgres-types` and `diesel` as optional dependencies, which is the mechanism behind the transparent mapping. The serde features decide a different question, namely what happens when a Decimal crosses a JSON boundary. A `serde-float` build serialises as a JSON number, a `serde-str` build as a string, and the arbitrary precision variants use the JSON mechanism that keeps the digits exact, with the `serde-with-` combinations letting you carry both a float and an exact string at once. For a financial system, that choice is not a detail, because a float round trip through JSON is exactly the round-off the crate exists to avoid.

What the repository tells you about how it is built

The crate is a modern Rust package with edition 2024 and a minimum supported Rust version of 1.85.0, authored by Paul Mason, and the version on this branch is 2.0.0-alpha.0. The repository layout shows the testing effort. There is a `fuzz/` directory, a `benches/` directory for benchmarks, a `tests/` directory that the manifest excludes from the published package only in its generated part, a `macros/` directory for the separate `rust_decimal_macros` crate, and a `build.rs` for the build script. The manifest also pulls in `proptest` and `rand` as optional dependencies, which is what property based testing of decimal arithmetic usually looks like, and the `examples/` directory holds a zero copy rkyv example and a set of serde JSON scenarios, the latter being the kind of fixture set that documents how a Decimal should survive a JSON round trip. The build is driven by a `Makefile.toml` for cargo-make, there is a `BUILD.md` and a `CHANGELOG.md`, and the formatting is pinned by a `rustfmt.toml`.

Picking a version, and where the documentation lives

The practical decision is between the two lines. The 1.x line is the one distributed to most users, with 1.43.0 released on 2026-09-02, 1.42.1 in June 2026 and 1.42.0 in May 2026, and the README says the stable branch still takes patches and bug fixes that remain backwards compatible. The main branch is where the API work happens, and the README asks contributors there to expect breakage. The last push to the repository was on 2026-10-18, so both lines are being worked on. The documentation is published on docs.rs with all features enabled, which matters when you are reading about a feature flag, because the docs then show the widest possible surface rather than the default one. The crates.io listing and the docs.rs badge are the two links the README puts at the top, and the crate is MIT licensed, which for a financial library is the licence you want, since it does not impose the copyleft obligations that would attach to a derived data store.

Editorial conclusion

Adopt rust_decimal if you need exact decimal arithmetic in Rust, particularly for money, where binary floating point is the wrong type, and pick the 1.x line if you need a stable, patchable dependency today, since the README says the main branch is unstable and that breaking changes are expected there. Do not start new work on the 2.0 alpha without reading the expected break list, which includes the removal of deprecated APIs, the removal of panics without explicit opt in, error type clean up for const and no_std support and module system experimentation. Before you adopt it, decide which serde mode you want, because the crate offers float, string and arbitrary precision variants, and check the database feature you need, since the Postgres and diesel integrations are separate features. The licence is MIT, the latest stable release is 1.43.0 from 2026-09-02, and the last push was on 2026-10-18.

Frequently asked questions

What is the difference between rust_decimal and a float for money?

The binary representation consists of a 96 bit integer number, a scaling factor used to specify the decimal fraction and a 1 bit sign, so there is no round-off error in the representation and trailing zeros are preserved. Because the value is stored as an integer and a scale rather than a mantissa and an exponent, trailing zeros may be exposed in string form and can be removed with `normalize` or handled with `round_dp`.

Should I use the main branch or the v1 branch of rust_decimal?

The README says the main branch is now considered unstable and that contributions to it may be breaking, with deprecated API removal, removal of panics without explicit opt in, error type clean up for const and no_std support, and module system experimentation listed as expected changes. Patch requests and bug fixes are still accepted against the `v1` branch, which holds the stable 1.x line and must stay backwards compatible.

How do I create a Decimal in rust_decimal?

With the `macros` feature you can use the `dec!` macro, which parses the input at compile time and turns invalid input into a compile time error, and it supports any Rust number format including scientific notation and alternate bases. Otherwise there are constructors such as `Decimal::new`, `from_i128_with_scale`, `from_str`, `from_str_radix`, `from_scientific_exact` and `from_parts`.

Does rust_decimal work in no_std and with databases?

Yes. The crate has an `alloc` feature for no_std environments that have an allocator, and database features for PostgreSQL, tokio-postgres, and diesel with PostgreSQL and MySQL, where the Postgres module transparently serialises and deserialises the type into the NUMERIC column type.

Official sources

  1. License: MIT
  2. paupino/rust-decimal on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/paupino-rust-decimal.svg)](https://hysenlabs.com/projects/paupino-rust-decimal)

Was this useful?