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).
Turn them on in Cargo.toml
:
i128
— i128
layout support which will be promoted to internally implemented I256
for
multiplication and division.
parity
— parity-scale-codec
support (Encode
and Decode
implementations).
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);
Method | Example (pseudo-code) | Description |
cadd | let result: Result<FixedPoint, ArithmeticError> = a.cadd(b) | Checked addition. Returns Err on overflow. |
csub | let result: Result<FixedPoint, ArithmeticError> = a.csub(b) | Checked subtraction. Returns Err on overflow. |
cmul | let 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. |
rmul | let 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. |
rdiv | let 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. |
cneg | let result: Result<FixedPoint, ArithmeticError> = a.cneg() | Checked negation. Returns Err on overflow (you can't negate MIN value). |
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 fixnum::ops::*;
let size = Size(4);
let price = fixnum!(4.25, 9);
let amount = size.cmul(price)?;
assert_eq!(amount, fixnum!(17, 9));
FixedPoint | Abstraction over fixed point numbers of arbitrary (but only compile-time specified) size
and precision.
|