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
,
i64
— promotes to i128
.
There's also support for i128
layout which will be promoted to internally implemented I256
— this is available
under the i128
feature.
use fixnum::{FixedPoint, typenum::U9, ops::{CheckedAdd, RoundingMul, RoundMode::*}};
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"));
assert_eq!(expences.rmul(expences, Ceil)?, expences);
It's possible to restrict the domain in order to reduce change 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 fixnum::ops::*;
let size = Size(2);
let price = Price("4.25".parse()?);
let amount = size.cmul(price)?;
assert_eq!(amount, Amount("8.5".parse()?));
FixedPoint | Abstraction over fixed point floating numbers.
|