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
,sub
are all implemented forConstDecimals
of 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 thedata
field without thescaling_factor
applied, 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) -> usize
returns 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 betweenNumDecimal
andConstDecimals
instances.
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 thedata
field value.
pub fn main() {
...
assert_eq!(addition.into_raw_units(), &BigUint::from(600u64));
}from_raw_units(data: BigUint<M>, decimals: D) -> Self
returns aManagedDecimal
from 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>) -> Self
returns aConstDecimals
type ofManagedDecimal
from 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) -> NumDecimals
returns 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 aManagedDecimal
from aBigInt
with customizablenum_decimals
.from_big_float<T: Decimals>(big_float: BigFloat<M>, num_decimals: T) -> ManagedDecimal<M, T>
constructs aManagedDecimal
from aBigFloat
with 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)
);
}