13 unstable releases (5 breaking)
0.8.1  Jun 28, 2023 

0.7.0  May 12, 2023 
0.6.0  Mar 14, 2023 
0.5.4  Aug 17, 2022 
0.2.0 

#34 in Math
1,048 downloads per month
Used in 3 crates
265KB
6.5K
SLoC
This crate provides a fast implementation of Decimal
fixedpoint
arithmetics.
It is targeted at typical business applications, dealing with numbers
representing quantities, money and the like, not at scientific computations,
for which the accuracy of floating point math is  in most cases  sufficient.
Objectives
 "Exact" representation of decimal numbers (no deviation as with binary floating point numbers)
 No hidden rounding errors (as inherent to floating point math)
 Very fast operations (by mapping them to integer ops)
 Range of representable decimal numbers sufficient for typical business applications
At the binary level a Decimal
number is represented as a coefficient (stored
as an i128
value) combined with a value specifying the number of fractional
decimal digits (stored as a u8
). The latter is limited to a value given by
the constant MAX_N_FRAC_DIGITS
= 18.
Status
Work in progess, but most of the API is stable.
Getting started
Add fpdec
to your Cargo.toml
:
[dependencies]
fpdec = "0.8"
Usage
A Decimal
number can be created in different ways.
The easiest method is to use the procedural macro Dec
:
let d = Dec!(17.5);
assert_eq!(d.to_string(), "17.5");
Alternatively you can convert an integer, a float or a string to a Decimal
:
let d = Decimal::from(297_i32);
assert_eq!(d.to_string(), "297");
let d = Decimal::try_from(83.25_f64)?;
assert_eq!(d.to_string(), "83.25");
let d = Decimal::from_str("38.2070")?;
assert_eq!(d.to_string(), "38.2070");
The sign of a Decimal
can be inverted using the unary minus operator and a
Decimal
instance can be compared to other instances of type Decimal
or all
basic types of integers (besides u128):
let x = Dec!(129.24);
let y = x;
assert_eq!(y.to_string(), "129.24");
assert!(129_i64 > y);
let z = y;
assert_eq!(x, z);
let z = Dec!(0.00097);
assert!(x > z);
assert!(y <= z);
assert!(z != 7_u32);
assert!(7_u32 == Dec!(7.00));
Decimal
supports all five binary numerical operators +, , *, /, and %, with
two Decimal
s or with a Decimal
and a basic integer (besides u128):
let x = Dec!(17.5);
let y = Dec!(6.40);
let z = x + y;
assert_eq!(z.to_string(), "23.90");
let z = x  y;
assert_eq!(z.to_string(), "11.10");
let z = x * y;
assert_eq!(z.to_string(), "112.000");
let z = x / y;
assert_eq!(z.to_string(), "2.734375");
let z = x % y;
assert_eq!(z.to_string(), "4.70");
let x = Dec!(17.5);
let y = 5_i64;
let z = x + y;
assert_eq!(z.to_string(), "12.5");
let z = x  y;
assert_eq!(z.to_string(), "22.5");
let z = y * x;
assert_eq!(z.to_string(), "87.5");
let z = x / y;
assert_eq!(z.to_string(), "3.5");
let z = x % y;
assert_eq!(z.to_string(), "2.5");
The results of Multiplication or Division are not exact in any case. If the
number of fractional decimal digits of the exact result would exceed
MAX_N_FRAC_DIGITS
fractional decimal digits, the result given is rounded to
fit this limit.
let x = Dec!(1e10);
let y = Dec!(75e9);
let z = x * y;
assert_eq!(z.to_string(), "0.000000000000000008");
let x = Dec!(1.);
let y = Dec!(3.);
let z = x / y;
assert_eq!(z.to_string(), "0.333333333333333333");
All these binary numeric operators panic if the result is not representable as
a Decimal
according to the constraints stated above. In addition, there are
functions implementing "checked" variants of the operators which return
Option::None
instead of panicking.
For Multiplication and Division there are also functions which return a result rounded to a given number of fractional digits:
let x = Dec!(17.5);
let y = Dec!(6.47);
let z: Decimal = x.mul_rounded(y, 1);
assert_eq!(z.to_string(), "113.2");
let z: Decimal = x.div_rounded(y, 3);
assert_eq!(z.to_string(), "2.705");
Crate features
By default, only the feature std
is enabled.
Ecosystem

std  When enabled, this will cause
fpdec
to use the standard library, so that conversion to string, formatting and printing are available. When disabled, the use of cratealloc
together with a systemspecific allocator is needed to use that functionality. 
packed  When enabled, the struct
Decimal
is marked with#[repr(packed)]
.
Optional dependencies

numtraits  When enabled, the trait
numtraits::Num
is implemented forDecimal
. 
serdeasstr  When enabled, support for
serde
is enabled. This allowsDecimal
instances to be serialzed as strings and to be deserialized from strings viaserde
. 
rkyv  When enabled, support for
rkyv
is enabled. This allowsDecimal
instances to be zerocopy serialized and deserialized viarkyv
archives.
Dependencies
~22–475KB
~10K SLoC