Crate fixnum[−][src]
Expand description
fixnum
Fixed-point numbers with explicit rounding.
Uses various signed integer types to store the number. The following are available by default:
i16
— promotes toi32
for multiplication and division,i32
— promotes toi64
(for mul, div),i64
— promotes toi128
(for mul, div).
Features
Turn them on in Cargo.toml
:
i128
—i128
layout support which will be promoted to internally implementedI256
for multiplication and division.parity
—parity-scale-codec
support (Encode
andDecode
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
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 rounding 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 rounding division. Returns Err on overflow. |
rsqrt | let result: Result<FixedPoint, ArithmeticError> = a.rsqrt(RoundMode::Floor) | Checked rounding square root. Returns Err for negative argument. |
cneg | let result: Result<FixedPoint, ArithmeticError> = a.cneg() | Checked negation. Returns Err on overflow (you can’t negate MIN value). |
integral | let y: {integer} = x.integral(RoundMode::Floor) | Takes rounded integral part of the number. |
saturating_add | let z: FixedPoint = x.saturating_add(y) | Saturating addition |
saturating_sub | let z: FixedPoint = x.saturating_sub(y) | Saturating subtraction |
saturating_mul | let 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_rmul | let 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
serde
A module that contains instances of Serialize
and Deserialize
for FixedPoint
.
Also contains submodule that can be provided to serde(with)
in order to
change the implementation.
Macros
Macro to create fixed-point “literals”. Contains .into()
call inside so you can use it with your
From<FixedPoint>
wrapper types.
Macro to create fixed-point const “literals”.
Structs
i128
or i64
or i32
or i16
Abstraction over fixed point numbers of arbitrary (but only compile-time specified) size and precision.