[][src]Crate fixnum

fixnum

Latest Version

FixedPoint 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).

There's also support for i128 layout which will be promoted to internally implemented I256 for multiplication and division — this is available under the i128 feature.

Example

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

/// 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>;

fn amount(s: &str) -> Amount { s.parse().unwrap() }

assert_eq!(amount("0.1").cadd(amount("0.2"))?, amount("0.3"));
let expences: Amount = amount("0.000000001");
assert_eq!(expences.rmul(expences, Floor)?, amount("0.0"));
// 1e-9 * (Ceil) 1e-9 = 1e-9
assert_eq!(expences.rmul(expences, Ceil)?, expences);

Available operations

Method NameExample (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 to integer types.
rmullet result: Result<FixedPoint, ArithmeticError> = a.rmul(b, RoundMode::Ceil)Checked rounded 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 rounded division. Returns Err on overflow. Because of provided RoundMode it's possible across the FixedPoint values.

Implementing wrapper types.

It's possible to restrict the domain in order to reduce chance of mistakes:

#[macro_use]
extern crate fixnum;
use fixnum::{impl_op, typenum::U9, FixedPoint};

type Fp64 = FixedPoint<i64, U9>;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
struct Size(i32);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
struct Price(Fp64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
struct PriceDelta(Fp64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
struct Amount(Fp64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
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(2);
let price = Price("4.25".parse()?);
let amount = size.cmul(price)?;
assert_eq!(amount, Amount("8.5".parse()?));

Re-exports

pub use typenum;

Modules

ops

Macros

impl_op

Structs

FixedPoint

Abstraction over fixed point floating numbers.

Enums

ArithmeticError
ConvertError
FromDecimalError

Traits

Precision