18 releases
| 0.9.3 | Jan 18, 2025 |
|---|---|
| 0.9.2 | Mar 2, 2023 |
| 0.9.1 | Oct 10, 2022 |
| 0.8.0 | Jun 23, 2022 |
| 0.2.3 | Dec 30, 2020 |
#300 in Rust patterns
17,572 downloads per month
Used in 2 crates
155KB
2.5K
SLoC
fixnum
Fixed-point numbers with explicit rounding.
lib.rs:
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));
Dependencies
~0.4–1.1MB
~25K SLoC