Skip to main content

Crate fin_decimal

Crate fin_decimal 

Source
Expand description

A Fixed-point decimal implementation written in Rust suitable for a wide range of financial calculations that require significant integral and fractional digits to follow decimal arithmetic rounding.

The binary representation consists of a 64 bit integer number, multiplied by power of 10 - default 10000 for ‘Amount64’ type.

Such implementation results in highly efficient addition and subtraction which are implemented as native operations on i64. These operations also don’t result in any rounding errors.

Multiplication and division are also relatively efficient and implemented via operations on i128 type internally. On some platforms (like x86-64) where 64 x 64 bit multiplication with 128 bit result and division of 128 bit integer by 64 bit are implemented as native instructions, performance penalty compared to regular i64 division and multiplication is negligible.

While 4 fractional decimal digits handles most of cases for accounting and tax computations, in some cases like exchange rates higher precision is desirable. To address this, a sibling type ‘Rate’ is introduced with 8 fractional digits.

§Wider backings: 128-bit and 256-bit

When the ~±922 trillion range of Amount64 is not enough, the same decimal semantics are available on wider backing integers:

Addition and subtraction remain native integer operations (one or two add/adc instructions). Multiplication builds the double-width intermediate from 64-bit limbs and re-scales it by the compile-time constant 10^DIGITS using Möller–Granlund reciprocal multiplication with a compile-time reciprocal — no division instructions and no compiler builtins on that path, on any architecture (scripts/check_asm.sh verifies the generated code). Division by another decimal (a runtime divisor) uses a 128-bit-by-64-bit division primitive — the native div instruction on x86_64 with the asm feature, or a portable long division on 32-bit half-digits — and Knuth’s algorithm D over the significant limbs for divisors wider than one limb.

Key operations (checked_mul, mul_rounded, round_to, trunc, powi, from_str_rounded, from_str_const, …) are const fn, so rates, fees and whole derived constants can be evaluated at compile time:

use fin_decimal::{Amount64, Rounding};
const PRICE: Amount64 = Amount64::from_str_const("19.99");
const RATE: Amount64 = Amount64::from_str_const("0.0825");
const TAX: Amount64 = PRICE.mul_rounded(RATE, Rounding::HalfEven);

The * and / operators of every backing panic on overflow regardless of build profile (they share one arithmetic core); use checked_mul/checked_div to get None instead.

§Usage

The stable version of rust requires you to create a Decimal number using one of its convenience methods.

use fin_decimal::Amount64;
use core::str::FromStr;

// Using an integer number.
let from_int = Amount64::from(3); // 3.0000

// Using a floating point number.
let from_f64 = Amount64::from(2.02f64); // 2.0200

// From a string representation
let from_string = Amount64::from_str("2.02").unwrap(); // 2.0200

// Using the `Into` trait
let my_int : Amount64 = 3i32.into();

Structs§

Decimal
Amount64 type implements decimal fixed-point arithmetic for financial computations. It is implemented to be as efficient as possible with most common add/sub operations to be native binary add/sub. Actual decimal processing is needed for multiplication and division where rounding should follow specific rules. Number of decimal points is chosen to be 4 - this seems to be enough for most use cases except for exchange rates where sometimes up to 8 decimal digits is required
Decimal128
Decimal fixed-point number backed by a 128-bit signed integer, with DIGITS fractional decimal digits (DIGITS <= 19).
Decimal256
Decimal fixed-point number backed by a 256-bit signed integer, with DIGITS fractional decimal digits (DIGITS <= 19).
I256
A 256-bit signed integer in two’s complement form, used as the backing storage of Decimal256. Only the operations the decimal type needs are provided; it is not a general-purpose big integer.

Enums§

AmountErrorKind
Enum to store the various types of errors that can cause parsing an integer to fail.
AmountSign
Defines how to display the sign of the parsed number.
Rounding
Rounding represents the different strategies that can be used.

Functions§

parse_decimal_i64
Converts a string in base 10 to a fixed-point scaled value. Can be used with non-default scale to handle higher precision exchange rates and other scenarios with longer fractional parts.
parse_decimal_i64_rounded
Converts a string in base 10 to a fixed-point scaled value, rounding any fractional digits beyond scale with the given Rounding mode.
str_i64
Converts an i64 to a fixed-point string representation, optionally padded.

Type Aliases§

Amount64
Amount64 is a 64-bit integer based decimal type with 4 decimal digits precision.
Amount128
Amount128 is a 128-bit integer based decimal type with 4 decimal digits precision.
Amount256
Amount256 is a 256-bit integer based decimal type with 4 decimal digits precision.
Rate64
Rate64 is a 64-bit integer based decimal type with 8 decimal digits precision.
Rate128
Rate128 is a 128-bit integer based decimal type with 8 decimal digits precision.
Rate256
Rate256 is a 256-bit integer based decimal type with 8 decimal digits precision.