Managed Decimal
Managed Decimal Overview
ManagedDecimal is a generic type representing a fixed-point decimal number managed by the API.
It is designed to handle decimal values with a specific number of decimals, providing operations such as addition, subtraction, multiplication, division, scaling, and conversion between different decimal precision and also more complex operations such as logarithm and nth root with a customizable degree of precision.
Essentially, ManagedDecimal is simply a struct containing a wrapper over a BigIntHandle (BigUint) and the number of decimals associated with that value (precision). Having such a light design helps ManagedDecimal become the optimal solution for dealing with fixed-point decimal numbers in terms of efficiency and readability.
#[derive(Debug, Clone)]
pub struct ManagedDecimal<M: ManagedTypeApi, D: Decimals> {
data: BigUint<M>, // value * scaling_factor
decimals: D, // number_of_decimals (precision)
}
The scaling factor of the decimal is 10^num_of_decimals and is a cached value across multiple ManagedDecimal instances for increased efficiency.
pub fn main() {
let decimal = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(1u64));
let cached_decimal = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(5u64));
}
Number of decimals
Decimals is a trait representing the number of decimals associated with a decimal value and is only
implemented for NumDecimals and ConstDecimals.
NumDecimals is a type alias for usize and is used to specify the number of decimals dynamically, while ConstDecimals is a type that represents a constant number of decimals and is used to statically specify the number of decimals for ManagedDecimal instances.
pub fn main() {
let decimal: ManagedDecimal<StaticApi, NumDecimals> = ManagedDecimal::from_raw_units(BigUint::from(100u64), 2usize);
let const_decimal = ManagedDecimal::<StaticApi, ConstDecimals<3>>::const_decimals_from_raw(BigUint::from(500u64))
}
Operations
ManagedDecimal supports various types of simple and complex operations, as well as conversions and scaling. More code examples can be found at mx-sdk-rs/framework/scenario/tests/managed_decimal_test.rs
Available methods:
-
Simple Operations:
add,mul,div,subare all implemented forConstDecimalsof the same scale.
pub fn main() {
let fixed = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(1u64));
let fixed_2 = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(5u64));
let addition = fixed + fixed_2;
assert_eq!(
addition,
ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(6u64))
);
}trunc(&self) -> BigUint<M>returns thedatafield without thescaling_factorapplied, by dividing the value to the scaling factor and truncating.
pub fn main() {
...
assert_eq!(addition.trunc(), BigUint::from(6u64));
} -
Complex Operations:
log<T: Decimals>(self, target_base: BigUint<M>, precision: T) -> ManagedDecimal<M, T>returns the value of log in any base with customizable precision level.
pub fn logarithm() {
let fixed = ManagedDecimal::<StaticApi, NumDecimals>::from_raw_units(BigUint::from(10u64), 1usize);
let fixed_const = ManagedDecimal::<StaticApi, ConstDecimals<1>>::const_decimals_from_raw(BigUint::from(10u64));
let log2_fixed = fixed.log(BigUint::from(2u64), 10_000usize);
assert_eq!(
log2_fixed,
ManagedDecimal::<StaticApi, NumDecimals>::from_raw_units(BigUint::from(33219u64), 10_000usize)
);
} -
Scaling:
scale(&self) -> usizereturns the number of decimals (the scale).scaling_factor<M: ManagedTypeApi>(&self) -> BigUint<M>returns the scaling factor value (10^num_decimals).rescale<T: Decimals>(self, scale_to: T) -> ManagedDecimal<M, T>returns the correspondent of the decimal in the newly specified scale. It can also convert betweenNumDecimalandConstDecimalsinstances.
pub fn main() {
...
let fixed_8: ManagedDecimal<StaticApi, NumDecimals> = ManagedDecimal::from_raw_units(BigUint::from(5u64), 5usize);
let fixed_9 = fixed_8.rescale(ConstDecimals::<3>);
assert_eq!(
fixed_9,
ManagedDecimal::<StaticApi, ConstDecimals<3>>::const_decimals_from_raw(BigUint::from(500u64))
);
} -
Conversions:
into_raw_units(&self) -> &BigUint<M>returns thedatafield value.
pub fn main() {
...
assert_eq!(addition.into_raw_units(), &BigUint::from(600u64));
}from_raw_units(data: BigUint<M>, decimals: D) -> Selfreturns aManagedDecimalfrom a data field value without applying scaling factor.
pub fn main() {
...
let fixed_4: ManagedDecimal<StaticApi, NumDecimals> = ManagedDecimal::from_raw_units(BigUint::from(100u64), 2usize);
let fixed_5 = fixed_4.rescale(2usize);
assert_eq!(
fixed_5,
ManagedDecimal::from_raw_units(BigUint::from(100000000u64), 8usize)
);
}const_decimals_from_raw(data: BigUint<M>) -> Selfreturns aConstDecimalstype ofManagedDecimalfrom a data field value without applying scaling factor.
pub fn main() {
...
let fixed_const: ManagedDecimal<StaticApi, ConstDecimals<1>> = ManagedDecimal::const_decimals_from_raw(BigUint::from(1u64));
}num_decimals(&self) -> NumDecimalsreturns the number of decimals.to_big_float(&self) -> BigFloat<M>returns the decimal asBigFloat<M>.to_big_int(self) -> BigInt<M>returns the decimal asBigInt.from_big_int<T: Decimals>(big_int: BigInt<M>, num_decimals: T) -> ManagedDecimal<M, T>constructs aManagedDecimalfrom aBigIntwith customizablenum_decimals.from_big_float<T: Decimals>(big_float: BigFloat<M>, num_decimals: T) -> ManagedDecimal<M, T>constructs aManagedDecimalfrom aBigFloatwith customizablenum_decimals.
pub fn main() {
...
let float_1 = BigFloat::<StaticApi>::from_frac(3i64, 2i64);
let fixed_float_1 = ManagedDecimal::<StaticApi, ConstDecimals<1>>::from_big_float(float_1.clone(),ConstDecimals::<1>);
let fixed_float_2 = ManagedDecimal::<StaticApi, NumDecimals>::from_big_float(float_1, 1usize);
assert_eq!(
fixed_float_1,
ManagedDecimal::<StaticApi, ConstDecimals<1>>::const_decimals_from_raw(BigUint::from(15u64))
);
assert_eq!(
fixed_float_2,
ManagedDecimal::<StaticApi, NumDecimals>::from_raw_units(BigUint::from(15u64), 1usize)
);
}