[][src]Crate fixnum

fixnum

Fixed-point numbers with explicit rounding.

Uses various signed integer types to store the number. The following are available by default:

  • i16 — promotes to i32 for multiplication and division,
  • i32 — promotes to i64 (for mul, div),
  • i64 — promotes to i128 (for mul, div).

Features

Turn them on in Cargo.toml:

  • i128i128 layout support which will be promoted to internally implemented I256 for multiplication and division.
  • parityparity-scale-codec support (Encode and Decode implementations).

Example

use fixnum::{FixedPoint, typenum::U9, ops::{CheckedAdd, RoundingMul, RoundMode::*, Zero}};

/// Signed fixed point amount over 64 bits, 9 decimal places.
///
/// MAX = (2 ^ (BITS_COUNT - 1) - 1) / 10 ^ PRECISION =
///     = (2 ^ (64 - 1) - 1) / 1e9 =
///     = 9223372036.854775807 ~ 9.2e9
/// ERROR_MAX = 0.5 / (10 ^ PRECISION) =
///           = 0.5 / 1e9 =
///           = 5e-10
type Amount = FixedPoint<i64, U9>;

let a: Amount = "0.1".parse()?;
let b: Amount = "0.2".parse()?;
assert_eq!(a.cadd(b)?, "0.3".parse()?);

let expences: Amount = "0.000000001".parse()?;
// 1e-9 * (Floor) 1e-9 = 0
assert_eq!(expences.rmul(expences, Floor)?, Amount::ZERO);
// 1e-9 * (Ceil) 1e-9 = 1e-9
assert_eq!(expences.rmul(expences, Ceil)?, expences);

Available operations

MethodExample (pseudo-code)Description
caddlet result: Result<FixedPoint, ArithmeticError> = a.cadd(b)Checked addition. Returns Err on overflow.
csublet result: Result<FixedPoint, ArithmeticError> = a.csub(b)Checked subtraction. Returns Err on overflow.
cmullet result: Result<FixedPoint, ArithmeticError> = a.cmul(b)Checked multiplication. Returns Err on overflow. This is multiplication without rounding, hence it's available only when at least one operand is integer.
rmullet result: Result<FixedPoint, ArithmeticError> = a.rmul(b, RoundMode::Ceil)Checked rounding multiplication. Returns Err on overflow. Because of provided RoundMode it's possible across the FixedPoint values.
rdivlet result: Result<FixedPoint, ArithmeticError> = a.rdiv(b, RoundMode::Floor)Checked rounding division. Returns Err on overflow. Because of provided RoundMode it's possible across the FixedPoint values.
cneglet result: Result<FixedPoint, ArithmeticError> = a.cneg()Checked negation. Returns Err on overflow (you can't negate MIN value).
integrallet y: {integer} = x.integral(RoundMode::Floor)Takes rounded integral part of the number.
saturating_addlet z: FixedPoint = x.saturating_add(y)Saturating addition
saturating_sublet z: FixedPoint = x.saturating_sub(y)Saturating subtraction
saturating_mullet z: FixedPoint = x.saturating_mul(y)Saturating multiplication. This is multiplication without rounding, hence it's available only when at least one operand is integer.
saturating_rmullet z: FixedPoint = x.saturating_rmul(y, RoundMode::Floor)Saturating rounding multiplication

Implementing wrapper types.

It's possible to restrict the domain in order to reduce chance of mistakes. Note that convenient fixnum! macro works with wrapper types too.

use derive_more::From;
use fixnum::{impl_op, typenum::U9, FixedPoint, fixnum};

type Fp64 = FixedPoint<i64, U9>;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, From)]
struct Size(i32);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, From)]
struct Price(Fp64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, From)]
struct PriceDelta(Fp64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, From)]
struct Amount(Fp64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, From)]
struct Ratio(Fp64);

impl_op!(Size [cadd] Size = Size);
impl_op!(Size [csub] Size = Size);
impl_op!(Size [rdiv] Size = Ratio);
impl_op!(Size [cmul] Price = Amount);
impl_op!(Price [csub] Price = PriceDelta);
impl_op!(Price [cadd] PriceDelta = Price);
impl_op!(Price [rdiv] Price = Ratio);
impl_op!(Price [rmul] Ratio = Price);
impl_op!(PriceDelta [cadd] PriceDelta = PriceDelta);
impl_op!(Amount [cadd] Amount = Amount);
impl_op!(Amount [csub] Amount = Amount);

// Use it.
use fixnum::ops::*;
let size = Size(4);
let price = fixnum!(4.25, 9); // compile-time
let amount = size.cmul(price)?;
assert_eq!(amount, fixnum!(17, 9));

Re-exports

pub use typenum;

Modules

ops

Macros

fixnum

Macro to create fixed-point "literals". Contains .into() call inside so you can use it with your From<FixedPoint> wrapper types.

fixnum_const

Macro to create fixed-point const "literals".

impl_op

Structs

FixedPoint

Abstraction over fixed point numbers of arbitrary (but only compile-time specified) size and precision.

Enums

ArithmeticError
ConvertError
FromDecimalError

Traits

Precision