Expand description

This crate provides a fast implementation of Decimal fixed-point 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.5"

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 Decimals 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!(1e-10);
let y = Dec!(75e-9);
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");

Macros

Macro used to convert a number literal into a Decimal.

Structs

Represents a decimal number as a coefficient (i128) combined with a value (u8) specifying the number of fractional decimal digits.

Enums

An error which can be returned from converting numbers to Decimal or from binary operators on Decimal.

An error which can be returned when parsing a decimal literal.

Enum representing the different methods used when rounding a number.

Constants

The maximum number of fractional decimal digits supported by Decimal.

Traits

Conversion of a number into an equivalent ratio of integers.

Checked addition. Computes self + rhs. Returns None if the result can not be represented by the Output type.

Checked division. Computes self / rhs. Returns None if the result can not be represented by the Output type.

Checked multiplication. Computes self * rhs. Returns None if the result can not be represented by the Output type.

Checked remainder. Computes self % rhs. Returns None if the result can not be represented by the Output type.

Checked subtraction. Computes self - rhs. Returns None if the result can not be represented by the Output type.

Division giving a result rounded to fit a given number of fractional digits.

Multiplication giving a result rounded to a given number of fractional digits.

Rounding a number to the nearest integer multiple of a given quantum.

Rounding a number to a given number of fractional digits.