Expand description
§fixnum
Fixed-point numbers with explicit rounding.
Uses various signed integer types to store the number.
§Features
Turn them on in Cargo.toml:
i128—i128layout support which will be promoted to a polyfill fori256for multiplication and division.i64—i64layout support which will be promoted toi128for multiplication and division.i32—i32layout support which will be promoted toi64for multiplication and division.i16—i16layout support which will be promoted toi32for multiplication and division.parity—parity-scale-codecsupport (EncodeandDecodeimplementations).serde— support forserde.schemars— support forschemars.std— Enabled by default.
At least one of i128, i64, i32, i16 must be enabled.
§Example
use fixnum::{FixedPoint, typenum::U9, ops::*, fixnum};
/// 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 = fixnum!(0.1, 9);
let b: Amount = fixnum!(0.2, 9);
assert_eq!(a.cadd(b)?, fixnum!(0.3, 9));
let expences: Amount = fixnum!(0.000000001, 9);
// 1e-9 * (Floor) 1e-9 = 0
assert_eq!(expences.rmul(expences, RoundMode::Floor)?, fixnum!(0, 9));
// 1e-9 * (Ceil) 1e-9 = 1e-9
assert_eq!(expences.rmul(expences, RoundMode::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§
- ops
- Contains traits for checked and rounding operations.
- serde
serde - A module that contains instances of
SerializeandDeserializeforFixedPoint. Also contains submodule that can be provided toserde(with)in order to change the implementation.
Macros§
- fixnum
- Macro to create fixed-point “literals”. Contains
.into()call inside so you can use it with yourFrom<FixedPoint>wrapper types. - fixnum_
const - Macro to create fixed-point const “literals”.
- impl_op
- Defines an operation for some wrapper. See top-level documentation.
Structs§
- Convert
Error - Represents errors during conversions.
- Fixed
Point i128ori64ori32ori16 - Abstraction over fixed point numbers of arbitrary (but only compile-time specified) size and precision.
Enums§
- Arithmetic
Error - Represents errors during arithmetic operations.
Traits§
- Precision
- The number of digits in the fractional part.