Skip to content
Snippets Groups Projects
fixed128.rs 8.46 KiB
Newer Older
  • Learn to ignore specific revisions
  • 邱昊's avatar
    邱昊 committed
    use codec::{Decode, Encode};
    use primitives::U256;
    use rstd::convert::{Into, TryFrom, TryInto};
    
    Xiliang Chen's avatar
    Xiliang Chen committed
    use sp_runtime::{
    
    	traits::{Bounded, Saturating},
    
    	Perbill, Percent, Permill, Perquintill,
    };
    
    邱昊's avatar
    邱昊 committed
    
    
    #[cfg(feature = "std")]
    use serde::{Deserialize, Serialize};
    
    
    邱昊's avatar
    邱昊 committed
    /// An unsigned fixed point number. Can hold any value in the range [0, 340_282_366_920_938_463_464]
    
    /// with fixed point accuracy of 10 ** 18.
    
    邱昊's avatar
    邱昊 committed
    #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
    
    #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
    
    邱昊's avatar
    邱昊 committed
    pub struct FixedU128(u128);
    
    const DIV: u128 = 1_000_000_000_000_000_000;
    
    impl FixedU128 {
    
    	/// Create self from a natural number.
    
    邱昊's avatar
    邱昊 committed
    	///
    
    	/// Note that this might be lossy.
    
    邱昊's avatar
    邱昊 committed
    	pub fn from_natural(int: u128) -> Self {
    		Self(int.saturating_mul(DIV))
    	}
    
    
    	/// Accuracy of `FixedU128`.
    	pub const fn accuracy() -> u128 {
    
    邱昊's avatar
    邱昊 committed
    		DIV
    	}
    
    
    	/// Raw constructor. Equal to `parts / DIV`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn from_parts(parts: u128) -> Self {
    		Self(parts)
    	}
    
    
    	/// Creates self from a rational number. Equal to `n/d`.
    
    邱昊's avatar
    邱昊 committed
    	///
    
    	/// Note that this might be lossy.
    
    邱昊's avatar
    邱昊 committed
    	pub fn from_rational(n: u128, d: u128) -> Self {
    		Self(
    			(U256::from(n).saturating_mul(U256::from(DIV)) / U256::from(d).max(U256::one()))
    				.try_into()
    				.unwrap_or_else(|_| Bounded::max_value()),
    		)
    	}
    
    
    	/// Consume self and return the inner raw `u128` value.
    
    邱昊's avatar
    邱昊 committed
    	///
    
    	/// Note this is a low level function, as the returned value is represented with accuracy.
    	pub fn deconstruct(self) -> u128 {
    
    邱昊's avatar
    邱昊 committed
    		self.0
    	}
    
    
    qiuhao's avatar
    qiuhao committed
    	/// Takes the reciprocal(inverse) of FixedU128, 1/x
    	pub fn recip(&self) -> Option<Self> {
    		Self::from_natural(1u128).checked_div(self)
    	}
    
    
    	/// Checked add. Same semantic to `num_traits::CheckedAdd`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
    		self.0.checked_add(rhs.0).map(Self)
    	}
    
    
    	/// Checked sub. Same semantic to `num_traits::CheckedSub`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
    		self.0.checked_sub(rhs.0).map(Self)
    	}
    
    
    	/// Checked mul. Same semantic to `num_traits::CheckedMul`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn checked_mul(&self, rhs: &Self) -> Option<Self> {
    		if let Some(r) = U256::from(self.0)
    			.checked_mul(U256::from(rhs.0))
    			.and_then(|n| n.checked_div(U256::from(DIV)))
    		{
    			if let Ok(r) = TryInto::<u128>::try_into(r) {
    				return Some(Self(r));
    			}
    		}
    
    		None
    	}
    
    
    	/// Checked div. Same semantic to `num_traits::CheckedDiv`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn checked_div(&self, rhs: &Self) -> Option<Self> {
    		if let Some(r) = U256::from(self.0)
    			.checked_mul(U256::from(DIV))
    			.and_then(|n| n.checked_div(U256::from(rhs.0)))
    		{
    			if let Ok(r) = TryInto::<u128>::try_into(r) {
    				return Some(Self(r));
    			}
    		}
    
    		None
    	}
    
    
    	/// Checked mul for int type `N`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn checked_mul_int<N>(&self, other: &N) -> Option<N>
    	where
    		N: Copy + TryFrom<u128> + TryInto<u128>,
    	{
    		if let Ok(n) = N::try_into(*other) {
    			if let Some(n) = U256::from(self.0)
    				.checked_mul(U256::from(n))
    				.and_then(|n| n.checked_div(U256::from(DIV)))
    			{
    				if let Ok(r) = TryInto::<u128>::try_into(n) {
    					if let Ok(r) = TryInto::<N>::try_into(r) {
    						return Some(r);
    					}
    				}
    			}
    		}
    
    		None
    	}
    
    
    	/// Checked div for int type `N`.
    
    邱昊's avatar
    邱昊 committed
    	pub fn checked_div_int<N>(&self, other: &N) -> Option<N>
    	where
    		N: Copy + TryFrom<u128> + TryInto<u128>,
    	{
    		if let Ok(n) = N::try_into(*other) {
    			if let Some(n) = self.0.checked_div(n).and_then(|n| n.checked_div(DIV)) {
    				if let Ok(r) = TryInto::<N>::try_into(n) {
    					return Some(r);
    				}
    			}
    		}
    
    		None
    	}
    }
    
    impl Saturating for FixedU128 {
    	fn saturating_add(self, rhs: Self) -> Self {
    		Self(self.0.saturating_add(rhs.0))
    	}
    
    	fn saturating_mul(self, rhs: Self) -> Self {
    		Self(
    			(U256::from(self.0).saturating_mul(U256::from(rhs.0)) / U256::from(DIV))
    				.try_into()
    				.unwrap_or_else(|_| Bounded::max_value()),
    		)
    	}
    
    	fn saturating_sub(self, rhs: Self) -> Self {
    		Self(self.0.saturating_sub(rhs.0))
    	}
    }
    
    impl Bounded for FixedU128 {
    	fn max_value() -> Self {
    		Self(u128::max_value())
    	}
    
    	fn min_value() -> Self {
    		Self(0u128)
    	}
    }
    
    impl rstd::fmt::Debug for FixedU128 {
    	#[cfg(feature = "std")]
    	fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
    		write!(f, "FixedU128({},{})", self.0 / DIV, (self.0 % DIV) / 1000)
    	}
    
    	#[cfg(not(feature = "std"))]
    	fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
    		Ok(())
    	}
    }
    
    
    macro_rules! impl_perthing_into_fixed_u128 {
    	($perthing:ty) => {
    		impl Into<FixedU128> for $perthing {
    			fn into(self) -> FixedU128 {
    				FixedU128::from_rational(self.deconstruct().into(), <$perthing>::accuracy().into())
    			}
    		}
    	};
    }
    
    impl_perthing_into_fixed_u128!(Percent);
    impl_perthing_into_fixed_u128!(Permill);
    impl_perthing_into_fixed_u128!(Perbill);
    impl_perthing_into_fixed_u128!(Perquintill);
    
    
    邱昊's avatar
    邱昊 committed
    #[cfg(test)]
    mod tests {
    	use super::*;
    
    	fn max() -> FixedU128 {
    		FixedU128::from_parts(u128::max_value())
    	}
    
    	#[test]
    	fn fixed128_semantics() {
    		assert_eq!(FixedU128::from_rational(5, 2).0, 5 * 1_000_000_000_000_000_000 / 2);
    		assert_eq!(FixedU128::from_rational(5, 2), FixedU128::from_rational(10, 4));
    		assert_eq!(FixedU128::from_rational(5, 0), FixedU128::from_rational(5, 1));
    
    		// biggest value that can be created.
    		assert_ne!(max(), FixedU128::from_natural(340_282_366_920_938_463_463));
    		assert_eq!(max(), FixedU128::from_natural(340_282_366_920_938_463_464));
    	}
    
    	#[test]
    	fn fixed128_operation() {
    		let a = FixedU128::from_natural(2);
    		let b = FixedU128::from_natural(1);
    		assert_eq!(a.checked_add(&b), Some(FixedU128::from_natural(1 + 2)));
    		assert_eq!(a.checked_sub(&b), Some(FixedU128::from_natural(2 - 1)));
    		assert_eq!(a.checked_mul(&b), Some(FixedU128::from_natural(1 * 2)));
    		assert_eq!(a.checked_div(&b), Some(FixedU128::from_rational(2, 1)));
    
    		let a = FixedU128::from_rational(5, 2);
    		let b = FixedU128::from_rational(3, 2);
    		assert_eq!(a.checked_add(&b), Some(FixedU128::from_rational(8, 2)));
    		assert_eq!(a.checked_sub(&b), Some(FixedU128::from_rational(2, 2)));
    		assert_eq!(a.checked_mul(&b), Some(FixedU128::from_rational(15, 4)));
    		assert_eq!(a.checked_div(&b), Some(FixedU128::from_rational(10, 6)));
    
    		let a = FixedU128::from_natural(120);
    		let b = 2i32;
    		assert_eq!(a.checked_div_int::<i32>(&b), Some(60));
    
    		let a = FixedU128::from_rational(20, 1);
    		let b = 2i32;
    		assert_eq!(a.checked_div_int::<i32>(&b), Some(10));
    
    		let a = FixedU128::from_natural(120);
    		let b = 2i32;
    		assert_eq!(a.checked_mul_int::<i32>(&b), Some(240));
    
    		let a = FixedU128::from_rational(1, 2);
    		let b = 20i32;
    		assert_eq!(a.checked_mul_int::<i32>(&b), Some(10));
    	}
    
    	#[test]
    	fn checked_div_with_zero_should_be_none() {
    		let a = FixedU128::from_natural(1);
    		let b = FixedU128::from_natural(0);
    		assert_eq!(a.checked_div(&b), None);
    	}
    
    	#[test]
    	fn checked_div_int_with_zero_should_be_none() {
    		let a = FixedU128::from_natural(1);
    		let b = 0i32;
    		assert_eq!(a.checked_div_int(&b), None);
    	}
    
    	#[test]
    	fn under_flow_should_be_none() {
    		let a = FixedU128::from_natural(2);
    		let b = FixedU128::from_natural(3);
    		assert_eq!(a.checked_sub(&b), None);
    	}
    
    	#[test]
    	fn over_flow_should_be_none() {
    		let a = FixedU128::from_parts(u128::max_value() - 1);
    		let b = FixedU128::from_parts(2);
    		assert_eq!(a.checked_add(&b), None);
    
    		let a = FixedU128::max_value();
    		let b = FixedU128::from_rational(2, 1);
    		assert_eq!(a.checked_mul(&b), None);
    
    		let a = FixedU128::from_natural(255);
    		let b = 2u8;
    		assert_eq!(a.checked_mul_int(&b), None);
    
    		let a = FixedU128::from_natural(256);
    		let b = 1u8;
    		assert_eq!(a.checked_div_int(&b), None);
    	}
    
    
    	#[test]
    	fn perthing_into_fixed_u128() {
    		let ten_percent_percent: FixedU128 = Percent::from_percent(10).into();
    
    		assert_eq!(ten_percent_percent.deconstruct(), DIV / 10);
    
    
    		let ten_percent_permill: FixedU128 = Permill::from_percent(10).into();
    
    		assert_eq!(ten_percent_permill.deconstruct(), DIV / 10);
    
    
    		let ten_percent_perbill: FixedU128 = Perbill::from_percent(10).into();
    
    		assert_eq!(ten_percent_perbill.deconstruct(), DIV / 10);
    
    
    		let ten_percent_perquintill: FixedU128 = Perquintill::from_percent(10).into();
    
    		assert_eq!(ten_percent_perquintill.deconstruct(), DIV / 10);
    
    qiuhao's avatar
    qiuhao committed
    
    	#[test]
    	fn recip_should_work() {
    		let a = FixedU128::from_natural(2);
    		assert_eq!(a.recip(), Some(FixedU128::from_rational(1, 2)));
    
    		let a = FixedU128::from_natural(2);
    		assert_eq!(a.recip().unwrap().checked_mul_int(&4i32), Some(2i32));
    
    		let a = FixedU128::from_rational(100, 121);
    		assert_eq!(a.recip(), Some(FixedU128::from_rational(121, 100)));
    
    		let a = FixedU128::from_rational(1, 2);
    		assert_eq!(a.recip().unwrap().checked_mul(&a), Some(FixedU128::from_natural(1)));
    
    		let a = FixedU128::from_natural(0);
    		assert_eq!(a.recip(), None);
    	}
    
    邱昊's avatar
    邱昊 committed
    }