Extend PerThing + Saturating (#5281)

* Extend PerThing + Saturating

* Add saturating_pow to Saturating
* Add saturating_truncating_mul to PerThing (rounding-down mul)
* Add saturating_reciprocal_mul to PerThing (divide x by perthing)
* Provide default methods where possible

* Restore const functions

* Fix test

* Update primitives/arithmetic/src/per_things.rs

Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Add comment and test verifying no overflow

* Formatting

* Fix possible overflow and change type constraint

* Use overflow pruning for all mul

* Formatting and comments

* Improve comments and names

* Comments in `rational_mul_correction` explain overflow aversion.

* Test rational_mul_correction

* Formatting

* Docs and formatting

* Add new trait methods to Perthing type impl

* Fix signature

* saturating_pow for Delegations

* Add missing trait method to impl

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Joseph Mark
2020-03-29 13:24:11 +02:00
committed by GitHub
parent d7ffef43ce
commit e8835d64a1
4 changed files with 536 additions and 151 deletions
+501 -141
View File
@@ -19,7 +19,9 @@ use serde::{Serialize, Deserialize};
use sp_std::{ops, fmt, prelude::*, convert::TryInto};
use codec::{Encode, Decode, CompactAs};
use crate::traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic, Bounded};
use crate::traits::{
SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic, Bounded, Zero,
};
use sp_debug_derive::RuntimeDebug;
/// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per
@@ -30,33 +32,145 @@ pub trait PerThing:
/// The data type used to build this per-thingy.
type Inner: BaseArithmetic + Copy + fmt::Debug;
/// The data type that is used to store values bigger than the maximum of this type. This must
/// at least be able to store `Self::ACCURACY * Self::ACCURACY`.
type Upper: BaseArithmetic + Copy + fmt::Debug;
/// A data type larger than `Self::Inner`, used to avoid overflow in some computations.
/// It must be able to compute `ACCURACY^2`.
type Upper: BaseArithmetic + Copy + From<Self::Inner> + TryInto<Self::Inner> + fmt::Debug;
/// accuracy of this type
/// The accuracy of this type.
const ACCURACY: Self::Inner;
/// NoThing
fn zero() -> Self;
/// Equivalent to `Self::from_parts(0)`.
fn zero() -> Self { Self::from_parts(Self::Inner::zero()) }
/// `true` if this is nothing.
fn is_zero(&self) -> bool;
/// Return `true` if this is nothing.
fn is_zero(&self) -> bool { self.deconstruct() == Self::Inner::zero() }
/// Everything.
fn one() -> Self;
/// Equivalent to `Self::from_parts(Self::ACCURACY)`.
fn one() -> Self { Self::from_parts(Self::ACCURACY) }
/// Consume self and deconstruct into a raw numeric type.
fn deconstruct(self) -> Self::Inner;
/// Return `true` if this is one.
fn is_one(&self) -> bool { self.deconstruct() == Self::ACCURACY }
/// From an explicitly defined number of parts per maximum of the type.
fn from_parts(parts: Self::Inner) -> Self;
/// Converts a percent into `Self`. Equal to `x / 100`.
fn from_percent(x: Self::Inner) -> Self;
/// Build this type from a percent. Equivalent to `Self::from_parts(x * Self::ACCURACY / 100)`
/// but more accurate.
fn from_percent(x: Self::Inner) -> Self {
let a = x.min(100.into());
let b = Self::ACCURACY;
// if Self::ACCURACY % 100 > 0 then we need the correction for accuracy
let c = rational_mul_correction::<Self::Inner, Self>(b, a, 100.into(), Rounding::Nearest);
Self::from_parts(a / 100.into() * b + c)
}
/// Return the product of multiplication of this value by itself.
fn square(self) -> Self;
fn square(self) -> Self {
let p = Self::Upper::from(self.deconstruct());
let q = Self::Upper::from(Self::ACCURACY);
Self::from_rational_approximation(p * p, q * q)
}
/// Multiplication that always rounds down to a whole number. The standard `Mul` rounds to the
/// nearest whole number.
///
/// ```rust
/// # use sp_arithmetic::{Percent, PerThing};
/// # fn main () {
/// // round to nearest
/// assert_eq!(Percent::from_percent(34) * 10u64, 3);
/// assert_eq!(Percent::from_percent(36) * 10u64, 4);
///
/// // round down
/// assert_eq!(Percent::from_percent(34).mul_floor(10u64), 3);
/// assert_eq!(Percent::from_percent(36).mul_floor(10u64), 3);
/// # }
/// ```
fn mul_floor<N>(self, b: N) -> N
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>
{
overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Down)
}
/// Multiplication that always rounds the result up to a whole number. The standard `Mul`
/// rounds to the nearest whole number.
///
/// ```rust
/// # use sp_arithmetic::{Percent, PerThing};
/// # fn main () {
/// // round to nearest
/// assert_eq!(Percent::from_percent(34) * 10u64, 3);
/// assert_eq!(Percent::from_percent(36) * 10u64, 4);
///
/// // round up
/// assert_eq!(Percent::from_percent(34).mul_ceil(10u64), 4);
/// assert_eq!(Percent::from_percent(36).mul_ceil(10u64), 4);
/// # }
/// ```
fn mul_ceil<N>(self, b: N) -> N
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>
{
overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Up)
}
/// Saturating multiplication by the reciprocal of `self`. The result is rounded to the
/// nearest whole number and saturates at the numeric bounds instead of overflowing.
///
/// ```rust
/// # use sp_arithmetic::{Percent, PerThing};
/// # fn main () {
/// assert_eq!(Percent::from_percent(50).saturating_reciprocal_mul(10u64), 20);
/// # }
/// ```
fn saturating_reciprocal_mul<N>(self, b: N) -> N
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> + Saturating
{
saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Nearest)
}
/// Saturating multiplication by the reciprocal of `self`. The result is rounded down to the
/// nearest whole number and saturates at the numeric bounds instead of overflowing.
///
/// ```rust
/// # use sp_arithmetic::{Percent, PerThing};
/// # fn main () {
/// // round to nearest
/// assert_eq!(Percent::from_percent(60).saturating_reciprocal_mul(10u64), 17);
/// // round down
/// assert_eq!(Percent::from_percent(60).saturating_reciprocal_mul_floor(10u64), 16);
/// # }
/// ```
fn saturating_reciprocal_mul_floor<N>(self, b: N) -> N
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> + Saturating
{
saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Down)
}
/// Saturating multiplication by the reciprocal of `self`. The result is rounded up to the
/// nearest whole number and saturates at the numeric bounds instead of overflowing.
///
/// ```rust
/// # use sp_arithmetic::{Percent, PerThing};
/// # fn main () {
/// // round to nearest
/// assert_eq!(Percent::from_percent(61).saturating_reciprocal_mul(10u64), 16);
/// // round up
/// assert_eq!(Percent::from_percent(61).saturating_reciprocal_mul_ceil(10u64), 17);
/// # }
/// ```
fn saturating_reciprocal_mul_ceil<N>(self, b: N) -> N
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> + Saturating
{
saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Up)
}
/// Consume self and return the number of parts per thing.
fn deconstruct(self) -> Self::Inner;
/// Build this type from a number of parts per thing.
fn from_parts(parts: Self::Inner) -> Self;
/// Converts a fraction into `Self`.
#[cfg(feature = "std")]
@@ -81,28 +195,106 @@ pub trait PerThing:
/// # }
/// ```
fn from_rational_approximation<N>(p: N, q: N) -> Self
where N:
Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + TryInto<Self::Upper> +
ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N>;
where N: Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + TryInto<Self::Upper> +
ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N>;
}
/// A mul implementation that always rounds down, whilst the standard `Mul` implementation
/// rounds to the nearest numbers
///
/// ```rust
/// # use sp_arithmetic::{Percent, PerThing};
/// # fn main () {
/// // rounds to closest
/// assert_eq!(Percent::from_percent(34) * 10u64, 3);
/// assert_eq!(Percent::from_percent(36) * 10u64, 4);
///
/// // collapse down
/// assert_eq!(Percent::from_percent(34).mul_collapse(10u64), 3);
/// assert_eq!(Percent::from_percent(36).mul_collapse(10u64), 3);
/// # }
/// ```
fn mul_collapse<N>(self, b: N) -> N
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N>
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>;
/// The rounding method to use.
///
/// `Perthing`s are unsigned so `Up` means towards infinity and `Down` means towards zero.
/// `Nearest` will round an exact half down.
enum Rounding {
Up,
Down,
Nearest,
}
/// Saturating reciprocal multiplication. Compute `x / self`, saturating at the numeric
/// bounds instead of overflowing.
fn saturating_reciprocal_mul<N, P>(
x: N,
part: P::Inner,
rounding: Rounding,
) -> N
where
N: Clone + From<P::Inner> + UniqueSaturatedInto<P::Inner> + ops::Div<N, Output=N> + ops::Mul<N,
Output=N> + ops::Add<N, Output=N> + ops::Rem<N, Output=N> + Saturating,
P: PerThing,
{
let maximum: N = P::ACCURACY.into();
let c = rational_mul_correction::<N, P>(
x.clone(),
P::ACCURACY,
part,
rounding,
);
(x / part.into()).saturating_mul(maximum).saturating_add(c)
}
/// Overflow-prune multiplication. Accurately multiply a value by `self` without overflowing.
fn overflow_prune_mul<N, P>(
x: N,
part: P::Inner,
rounding: Rounding,
) -> N
where
N: Clone + From<P::Inner> + UniqueSaturatedInto<P::Inner> + ops::Div<N, Output=N> + ops::Mul<N,
Output=N> + ops::Add<N, Output=N> + ops::Rem<N, Output=N>,
P: PerThing,
{
let maximum: N = P::ACCURACY.into();
let part_n: N = part.into();
let c = rational_mul_correction::<N, P>(
x.clone(),
part,
P::ACCURACY,
rounding,
);
(x / maximum) * part_n + c
}
/// Compute the error due to integer division in the expression `x / denom * numer`.
///
/// Take the remainder of `x / denom` and multiply by `numer / denom`. The result can be added
/// to `x / denom * numer` for an accurate result.
fn rational_mul_correction<N, P>(
x: N,
numer: P::Inner,
denom: P::Inner,
rounding: Rounding,
) -> N
where
N: From<P::Inner> + UniqueSaturatedInto<P::Inner> + ops::Div<N, Output=N> + ops::Mul<N,
Output=N> + ops::Add<N, Output=N> + ops::Rem<N, Output=N>,
P: PerThing,
{
let numer_upper = P::Upper::from(numer);
let denom_n = N::from(denom);
let denom_upper = P::Upper::from(denom);
let rem = x.rem(denom_n);
// `rem` is less than `denom`, which fits in `P::Inner`.
let rem_inner = rem.saturated_into::<P::Inner>();
// `P::Upper` always fits `P::Inner::max_value().pow(2)`, thus it fits `rem * numer`.
let rem_mul_upper = P::Upper::from(rem_inner) * numer_upper;
// `rem` is less than `denom`, so `rem * numer / denom` is less than `numer`, which fits in
// `P::Inner`.
let mut rem_mul_div_inner = (rem_mul_upper / denom_upper).saturated_into::<P::Inner>();
match rounding {
// Already rounded down
Rounding::Down => {},
// Round up if the fractional part of the result is non-zero.
Rounding::Up => if rem_mul_upper % denom_upper > 0.into() {
// `rem * numer / denom` is less than `numer`, so this will not overflow.
rem_mul_div_inner = rem_mul_div_inner + 1.into();
},
// Round up if the fractional part of the result is greater than a half. An exact half is
// rounded down.
Rounding::Nearest => if rem_mul_upper % denom_upper > denom_upper / 2.into() {
// `rem * numer / denom` is less than `numer`, so this will not overflow.
rem_mul_div_inner = rem_mul_div_inner + 1.into();
},
}
rem_mul_div_inner.into()
}
macro_rules! implement_per_thing {
@@ -110,15 +302,17 @@ macro_rules! implement_per_thing {
$name:ident,
$test_mod:ident,
[$($test_units:tt),+],
$max:tt, $type:ty,
$max:tt,
$type:ty,
$upper_type:ty,
$title:expr $(,)?
) => {
/// A fixed point representation of a number between in the range [0, 1].
/// A fixed point representation of a number in the range [0, 1].
///
#[doc = $title]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)]
#[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord,
RuntimeDebug, CompactAs)]
pub struct $name($type);
impl PerThing for $name {
@@ -127,40 +321,20 @@ macro_rules! implement_per_thing {
const ACCURACY: Self::Inner = $max;
fn zero() -> Self { Self(0) }
fn is_zero(&self) -> bool { self.0 == 0 }
fn one() -> Self { Self($max) }
/// Consume self and return the number of parts per thing.
fn deconstruct(self) -> Self::Inner { self.0 }
// needed only for peru16. Since peru16 is the only type in which $max ==
// $type::max_value(), rustc is being a smart-a** here by warning that the comparison
// is not needed.
#[allow(unused_comparisons)]
fn from_parts(parts: Self::Inner) -> Self {
Self([parts, $max][(parts > $max) as usize])
}
fn from_percent(x: Self::Inner) -> Self {
Self::from_rational_approximation([x, 100][(x > 100) as usize] as $upper_type, 100)
}
fn square(self) -> Self {
// both can be safely casted and multiplied.
let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type;
let q: $upper_type = <$upper_type>::from($max) * <$upper_type>::from($max);
Self::from_rational_approximation(p, q)
}
/// Build this type from a number of parts per thing.
fn from_parts(parts: Self::Inner) -> Self { Self(parts.min($max)) }
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as Self::Inner) }
fn from_fraction(x: f64) -> Self {
Self::from_parts((x * $max as f64) as Self::Inner)
}
fn from_rational_approximation<N>(p: N, q: N) -> Self
where N:
Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + TryInto<Self::Upper> +
ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N>
where N: Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + TryInto<Self::Upper>
+ ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N>
{
let div_ceil = |x: N, f: N| -> N {
let mut o = x.clone() / f.clone();
@@ -203,39 +377,6 @@ macro_rules! implement_per_thing {
$name(part as Self::Inner)
}
fn mul_collapse<N>(self, b: N) -> N
where
N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N>
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>
{
let maximum: N = $max.into();
let upper_max: $upper_type = $max.into();
let part: N = self.0.into();
let rem_multiplied_divided = {
let rem = b.clone().rem(maximum.clone());
// `rem_sized` is inferior to $max, thus it fits into $type. This is assured by
// a test.
let rem_sized = rem.saturated_into::<$type>();
// `self` and `rem_sized` are inferior to $max, thus the product is less than
// $max^2 and fits into $upper_type. This is assured by a test.
let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type;
// `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits
// in $type. remember that $type always fits $max.
let rem_multiplied_divided_sized =
(rem_multiplied_upper / upper_max) as $type;
// `rem_multiplied_divided_sized` is inferior to b, thus it can be converted
// back to N type
rem_multiplied_divided_sized.into()
};
(b / maximum) * part + rem_multiplied_divided
}
}
impl $name {
@@ -254,7 +395,7 @@ macro_rules! implement_per_thing {
///
/// This can be created at compile time.
pub const fn from_percent(x: $type) -> Self {
Self([x, 100][(x > 100) as usize] * ($max / 100))
Self(([x, 100][(x > 100) as usize] as $upper_type * $max as $upper_type / 100) as $type)
}
/// See [`PerThing::one`].
@@ -262,6 +403,11 @@ macro_rules! implement_per_thing {
<Self as PerThing>::one()
}
/// See [`PerThing::is_one`].
pub fn is_one(&self) -> bool {
PerThing::is_one(self)
}
/// See [`PerThing::zero`].
pub fn zero() -> Self {
<Self as PerThing>::zero()
@@ -296,23 +442,63 @@ macro_rules! implement_per_thing {
<Self as PerThing>::from_rational_approximation(p, q)
}
/// See [`PerThing::mul_collapse`].
pub fn mul_collapse<N>(self, b: N) -> N
/// See [`PerThing::mul_floor`].
pub fn mul_floor<N>(self, b: N) -> N
where N: Clone + From<$type> + UniqueSaturatedInto<$type> +
ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
ops::Add<N, Output=N> {
PerThing::mul_collapse(self, b)
PerThing::mul_floor(self, b)
}
/// See [`PerThing::mul_ceil`].
pub fn mul_ceil<N>(self, b: N) -> N
where N: Clone + From<$type> + UniqueSaturatedInto<$type> +
ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
ops::Add<N, Output=N> {
PerThing::mul_ceil(self, b)
}
/// See [`PerThing::saturating_reciprocal_mul`].
fn saturating_reciprocal_mul<N>(self, b: N) -> N
where N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> +
Saturating {
PerThing::saturating_reciprocal_mul(self, b)
}
/// See [`PerThing::saturating_reciprocal_mul_floor`].
fn saturating_reciprocal_mul_floor<N>(self, b: N) -> N
where N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> +
Saturating {
PerThing::saturating_reciprocal_mul_floor(self, b)
}
/// See [`PerThing::saturating_reciprocal_mul_ceil`].
fn saturating_reciprocal_mul_ceil<N>(self, b: N) -> N
where N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N> +
ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> +
Saturating {
PerThing::saturating_reciprocal_mul_ceil(self, b)
}
}
impl Saturating for $name {
/// Saturating addition. Compute `self + rhs`, saturating at the numeric bounds instead of
/// overflowing. This operation is lossless if it does not saturate.
fn saturating_add(self, rhs: Self) -> Self {
// defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow.
Self::from_parts(self.0.saturating_add(rhs.0))
}
/// Saturating subtraction. Compute `self - rhs`, saturating at the numeric bounds instead of
/// overflowing. This operation is lossless if it does not saturate.
fn saturating_sub(self, rhs: Self) -> Self {
Self::from_parts(self.0.saturating_sub(rhs.0))
}
/// Saturating multiply. Compute `self * rhs`, saturating at the numeric bounds instead of
/// overflowing. This operation is lossy.
fn saturating_mul(self, rhs: Self) -> Self {
let a = self.0 as $upper_type;
let b = rhs.0 as $upper_type;
@@ -321,6 +507,31 @@ macro_rules! implement_per_thing {
// This will always fit into $type.
Self::from_parts(parts as $type)
}
/// Saturating exponentiation. Computes `self.pow(exp)`, saturating at the numeric
/// bounds instead of overflowing. This operation is lossy.
fn saturating_pow(self, exp: usize) -> Self {
if self.is_zero() || self.is_one() {
self
} else {
let p = <$name as PerThing>::Upper::from(self.deconstruct());
let q = <$name as PerThing>::Upper::from(Self::ACCURACY);
let mut s = Self::one();
for _ in 0..exp {
if s.is_zero() {
break;
} else {
// x^2 always fits in Self::Upper if x fits in Self::Inner.
// Verified by a test.
s = Self::from_rational_approximation(
<$name as PerThing>::Upper::from(s.deconstruct()) * p,
q * q,
);
}
}
s
}
}
}
impl crate::traits::Bounded for $name {
@@ -345,8 +556,7 @@ macro_rules! implement_per_thing {
/// Non-overflow multiplication.
///
/// tailored to be used with a balance type.
///
/// This is tailored to be used with a balance type.
impl<N> ops::Mul<N> for $name
where
N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N>
@@ -354,37 +564,7 @@ macro_rules! implement_per_thing {
{
type Output = N;
fn mul(self, b: N) -> Self::Output {
let maximum: N = $max.into();
let upper_max: $upper_type = $max.into();
let part: N = self.0.into();
let rem_multiplied_divided = {
let rem = b.clone().rem(maximum.clone());
// `rem_sized` is inferior to $max, thus it fits into $type. This is assured by
// a test.
let rem_sized = rem.saturated_into::<$type>();
// `self` and `rem_sized` are inferior to $max, thus the product is less than
// $max^2 and fits into $upper_type. This is assured by a test.
let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type;
// `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits
// in $type. remember that $type always fits $max.
let mut rem_multiplied_divided_sized =
(rem_multiplied_upper / upper_max) as $type;
// fix a tiny rounding error
if rem_multiplied_upper % upper_max > upper_max / 2 {
rem_multiplied_divided_sized += 1;
}
// `rem_multiplied_divided_sized` is inferior to b, thus it can be converted
// back to N type
rem_multiplied_divided_sized.into()
};
(b / maximum) * part + rem_multiplied_divided
overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Nearest)
}
}
@@ -410,6 +590,9 @@ macro_rules! implement_per_thing {
// for something like percent they can be the same.
assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value());
assert!(<$upper_type>::from($max).checked_mul($max.into()).is_some());
// make sure saturating_pow won't overflow the upper type
assert!(<$upper_type>::from($max) * <$upper_type>::from($max) < <$upper_type>::max_value());
}
#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)]
@@ -452,6 +635,12 @@ macro_rules! implement_per_thing {
assert_eq!($name::zero(), $name::from_parts(Zero::zero()));
assert_eq!($name::one(), $name::from_parts($max));
assert_eq!($name::ACCURACY, $max);
assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero()));
assert_eq!($name::from_percent(10), $name::from_parts($max / 10));
assert_eq!($name::from_percent(100), $name::from_parts($max));
assert_eq!($name::from_percent(200), $name::from_parts($max));
assert_eq!($name::from_fraction(0.0), $name::from_parts(Zero::zero()));
assert_eq!($name::from_fraction(0.1), $name::from_parts($max / 10));
assert_eq!($name::from_fraction(1.0), $name::from_parts($max));
@@ -742,6 +931,177 @@ macro_rules! implement_per_thing {
2,
);
}
#[test]
fn saturating_pow_works() {
// x^0 == 1
assert_eq!(
$name::from_parts($max / 2).saturating_pow(0),
$name::from_parts($max),
);
// x^1 == x
assert_eq!(
$name::from_parts($max / 2).saturating_pow(1),
$name::from_parts($max / 2),
);
// x^2
assert_eq!(
$name::from_parts($max / 2).saturating_pow(2),
$name::from_parts($max / 2).square(),
);
// x^3
assert_eq!(
$name::from_parts($max / 2).saturating_pow(3),
$name::from_parts($max / 8),
);
// 0^n == 0
assert_eq!(
$name::from_parts(0).saturating_pow(3),
$name::from_parts(0),
);
// 1^n == 1
assert_eq!(
$name::from_parts($max).saturating_pow(3),
$name::from_parts($max),
);
// (x < 1)^inf == 0 (where 2.pow(31) ~ inf)
assert_eq!(
$name::from_parts($max / 2).saturating_pow(2usize.pow(31)),
$name::from_parts(0),
);
}
#[test]
fn saturating_reciprocal_mul_works() {
// divide by 1
assert_eq!(
$name::from_parts($max).saturating_reciprocal_mul(<$type>::from(10u8)),
10,
);
// divide by 1/2
assert_eq!(
$name::from_parts($max / 2).saturating_reciprocal_mul(<$type>::from(10u8)),
20,
);
// saturate
assert_eq!(
$name::from_parts(1).saturating_reciprocal_mul($max),
<$type>::max_value(),
);
// round to nearest
assert_eq!(
$name::from_percent(60).saturating_reciprocal_mul(<$type>::from(10u8)),
17,
);
// round down
assert_eq!(
$name::from_percent(60).saturating_reciprocal_mul_floor(<$type>::from(10u8)),
16,
);
// round to nearest
assert_eq!(
$name::from_percent(61).saturating_reciprocal_mul(<$type>::from(10u8)),
16,
);
// round up
assert_eq!(
$name::from_percent(61).saturating_reciprocal_mul_ceil(<$type>::from(10u8)),
17,
);
}
#[test]
fn saturating_truncating_mul_works() {
assert_eq!(
$name::from_percent(49).mul_floor(10 as $type),
4,
);
let a: $upper_type = $name::from_percent(50).mul_floor(($max as $upper_type).pow(2));
let b: $upper_type = ($max as $upper_type).pow(2) / 2;
if $max % 2 == 0 {
assert_eq!(a, b);
} else {
// difference should be less that 1%, IE less than the error in `from_percent`
assert!(b - a < ($max as $upper_type).pow(2) / 100 as $upper_type);
}
}
#[test]
fn rational_mul_correction_works() {
assert_eq!(
super::rational_mul_correction::<$type, $name>(
<$type>::max_value(),
<$type>::max_value(),
<$type>::max_value(),
super::Rounding::Nearest,
),
0,
);
assert_eq!(
super::rational_mul_correction::<$type, $name>(
<$type>::max_value() - 1,
<$type>::max_value(),
<$type>::max_value(),
super::Rounding::Nearest,
),
<$type>::max_value() - 1,
);
assert_eq!(
super::rational_mul_correction::<$upper_type, $name>(
((<$type>::max_value() - 1) as $upper_type).pow(2),
<$type>::max_value(),
<$type>::max_value(),
super::Rounding::Nearest,
),
1,
);
// ((max^2 - 1) % max) * max / max == max - 1
assert_eq!(
super::rational_mul_correction::<$upper_type, $name>(
(<$type>::max_value() as $upper_type).pow(2) - 1,
<$type>::max_value(),
<$type>::max_value(),
super::Rounding::Nearest,
),
(<$type>::max_value() - 1).into(),
);
// (max % 2) * max / 2 == max / 2
assert_eq!(
super::rational_mul_correction::<$upper_type, $name>(
(<$type>::max_value() as $upper_type).pow(2),
<$type>::max_value(),
2 as $type,
super::Rounding::Nearest,
),
<$type>::max_value() as $upper_type / 2,
);
// ((max^2 - 1) % max) * 2 / max == 2 (rounded up)
assert_eq!(
super::rational_mul_correction::<$upper_type, $name>(
(<$type>::max_value() as $upper_type).pow(2) - 1,
2 as $type,
<$type>::max_value(),
super::Rounding::Nearest,
),
2,
);
// ((max^2 - 1) % max) * 2 / max == 1 (rounded down)
assert_eq!(
super::rational_mul_correction::<$upper_type, $name>(
(<$type>::max_value() as $upper_type).pow(2) - 1,
2 as $type,
<$type>::max_value(),
super::Rounding::Down,
),
1,
);
}
}
};
}