mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 15:21:05 +00:00
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:
@@ -62,6 +62,13 @@ impl<Balance: Saturating> Saturating for Delegations<Balance> {
|
|||||||
capital: self.capital.saturating_mul(o.capital),
|
capital: self.capital.saturating_mul(o.capital),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn saturating_pow(self, exp: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
votes: self.votes.saturating_pow(exp),
|
||||||
|
capital: self.capital.saturating_pow(exp),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
|
|||||||
@@ -110,12 +110,18 @@ impl Saturating for Fixed64 {
|
|||||||
fn saturating_add(self, rhs: Self) -> Self {
|
fn saturating_add(self, rhs: Self) -> Self {
|
||||||
Self(self.0.saturating_add(rhs.0))
|
Self(self.0.saturating_add(rhs.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn saturating_mul(self, rhs: Self) -> Self {
|
fn saturating_mul(self, rhs: Self) -> Self {
|
||||||
Self(self.0.saturating_mul(rhs.0) / DIV)
|
Self(self.0.saturating_mul(rhs.0) / DIV)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn saturating_sub(self, rhs: Self) -> Self {
|
fn saturating_sub(self, rhs: Self) -> Self {
|
||||||
Self(self.0.saturating_sub(rhs.0))
|
Self(self.0.saturating_sub(rhs.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn saturating_pow(self, exp: usize) -> Self {
|
||||||
|
Self(self.0.saturating_pow(exp as u32))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait
|
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
use sp_std::{ops, fmt, prelude::*, convert::TryInto};
|
use sp_std::{ops, fmt, prelude::*, convert::TryInto};
|
||||||
use codec::{Encode, Decode, CompactAs};
|
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;
|
use sp_debug_derive::RuntimeDebug;
|
||||||
|
|
||||||
/// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per
|
/// 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.
|
/// The data type used to build this per-thingy.
|
||||||
type Inner: BaseArithmetic + Copy + fmt::Debug;
|
type Inner: BaseArithmetic + Copy + fmt::Debug;
|
||||||
|
|
||||||
/// The data type that is used to store values bigger than the maximum of this type. This must
|
/// A data type larger than `Self::Inner`, used to avoid overflow in some computations.
|
||||||
/// at least be able to store `Self::ACCURACY * Self::ACCURACY`.
|
/// It must be able to compute `ACCURACY^2`.
|
||||||
type Upper: BaseArithmetic + Copy + fmt::Debug;
|
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;
|
const ACCURACY: Self::Inner;
|
||||||
|
|
||||||
/// NoThing
|
/// Equivalent to `Self::from_parts(0)`.
|
||||||
fn zero() -> Self;
|
fn zero() -> Self { Self::from_parts(Self::Inner::zero()) }
|
||||||
|
|
||||||
/// `true` if this is nothing.
|
/// Return `true` if this is nothing.
|
||||||
fn is_zero(&self) -> bool;
|
fn is_zero(&self) -> bool { self.deconstruct() == Self::Inner::zero() }
|
||||||
|
|
||||||
/// Everything.
|
/// Equivalent to `Self::from_parts(Self::ACCURACY)`.
|
||||||
fn one() -> Self;
|
fn one() -> Self { Self::from_parts(Self::ACCURACY) }
|
||||||
|
|
||||||
/// Consume self and deconstruct into a raw numeric type.
|
/// Return `true` if this is one.
|
||||||
fn deconstruct(self) -> Self::Inner;
|
fn is_one(&self) -> bool { self.deconstruct() == Self::ACCURACY }
|
||||||
|
|
||||||
/// From an explicitly defined number of parts per maximum of the type.
|
/// Build this type from a percent. Equivalent to `Self::from_parts(x * Self::ACCURACY / 100)`
|
||||||
fn from_parts(parts: Self::Inner) -> Self;
|
/// but more accurate.
|
||||||
|
fn from_percent(x: Self::Inner) -> Self {
|
||||||
/// Converts a percent into `Self`. Equal to `x / 100`.
|
let a = x.min(100.into());
|
||||||
fn from_percent(x: Self::Inner) -> Self;
|
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.
|
/// 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`.
|
/// Converts a fraction into `Self`.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -81,28 +195,106 @@ pub trait PerThing:
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
fn from_rational_approximation<N>(p: N, q: N) -> Self
|
fn from_rational_approximation<N>(p: N, q: N) -> Self
|
||||||
where N:
|
where N: Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + TryInto<Self::Upper> +
|
||||||
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>;
|
||||||
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
|
/// The rounding method to use.
|
||||||
/// rounds to the nearest numbers
|
///
|
||||||
///
|
/// `Perthing`s are unsigned so `Up` means towards infinity and `Down` means towards zero.
|
||||||
/// ```rust
|
/// `Nearest` will round an exact half down.
|
||||||
/// # use sp_arithmetic::{Percent, PerThing};
|
enum Rounding {
|
||||||
/// # fn main () {
|
Up,
|
||||||
/// // rounds to closest
|
Down,
|
||||||
/// assert_eq!(Percent::from_percent(34) * 10u64, 3);
|
Nearest,
|
||||||
/// assert_eq!(Percent::from_percent(36) * 10u64, 4);
|
}
|
||||||
///
|
|
||||||
/// // collapse down
|
/// Saturating reciprocal multiplication. Compute `x / self`, saturating at the numeric
|
||||||
/// assert_eq!(Percent::from_percent(34).mul_collapse(10u64), 3);
|
/// bounds instead of overflowing.
|
||||||
/// assert_eq!(Percent::from_percent(36).mul_collapse(10u64), 3);
|
fn saturating_reciprocal_mul<N, P>(
|
||||||
/// # }
|
x: N,
|
||||||
/// ```
|
part: P::Inner,
|
||||||
fn mul_collapse<N>(self, b: N) -> N
|
rounding: Rounding,
|
||||||
where N: Clone + From<Self::Inner> + UniqueSaturatedInto<Self::Inner> + ops::Rem<N, Output=N>
|
) -> N
|
||||||
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=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 {
|
macro_rules! implement_per_thing {
|
||||||
@@ -110,15 +302,17 @@ macro_rules! implement_per_thing {
|
|||||||
$name:ident,
|
$name:ident,
|
||||||
$test_mod:ident,
|
$test_mod:ident,
|
||||||
[$($test_units:tt),+],
|
[$($test_units:tt),+],
|
||||||
$max:tt, $type:ty,
|
$max:tt,
|
||||||
|
$type:ty,
|
||||||
$upper_type:ty,
|
$upper_type:ty,
|
||||||
$title:expr $(,)?
|
$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]
|
#[doc = $title]
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[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);
|
pub struct $name($type);
|
||||||
|
|
||||||
impl PerThing for $name {
|
impl PerThing for $name {
|
||||||
@@ -127,40 +321,20 @@ macro_rules! implement_per_thing {
|
|||||||
|
|
||||||
const ACCURACY: Self::Inner = $max;
|
const ACCURACY: Self::Inner = $max;
|
||||||
|
|
||||||
fn zero() -> Self { Self(0) }
|
/// Consume self and return the number of parts per thing.
|
||||||
|
|
||||||
fn is_zero(&self) -> bool { self.0 == 0 }
|
|
||||||
|
|
||||||
fn one() -> Self { Self($max) }
|
|
||||||
|
|
||||||
fn deconstruct(self) -> Self::Inner { self.0 }
|
fn deconstruct(self) -> Self::Inner { self.0 }
|
||||||
|
|
||||||
// needed only for peru16. Since peru16 is the only type in which $max ==
|
/// Build this type from a number of parts per thing.
|
||||||
// $type::max_value(), rustc is being a smart-a** here by warning that the comparison
|
fn from_parts(parts: Self::Inner) -> Self { Self(parts.min($max)) }
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[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
|
fn from_rational_approximation<N>(p: N, q: N) -> Self
|
||||||
where N:
|
where N: Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + TryInto<Self::Upper>
|
||||||
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>
|
||||||
ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N>
|
|
||||||
{
|
{
|
||||||
let div_ceil = |x: N, f: N| -> N {
|
let div_ceil = |x: N, f: N| -> N {
|
||||||
let mut o = x.clone() / f.clone();
|
let mut o = x.clone() / f.clone();
|
||||||
@@ -203,39 +377,6 @@ macro_rules! implement_per_thing {
|
|||||||
|
|
||||||
$name(part as Self::Inner)
|
$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 {
|
impl $name {
|
||||||
@@ -254,7 +395,7 @@ macro_rules! implement_per_thing {
|
|||||||
///
|
///
|
||||||
/// This can be created at compile time.
|
/// This can be created at compile time.
|
||||||
pub const fn from_percent(x: $type) -> Self {
|
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`].
|
/// See [`PerThing::one`].
|
||||||
@@ -262,6 +403,11 @@ macro_rules! implement_per_thing {
|
|||||||
<Self as PerThing>::one()
|
<Self as PerThing>::one()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See [`PerThing::is_one`].
|
||||||
|
pub fn is_one(&self) -> bool {
|
||||||
|
PerThing::is_one(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// See [`PerThing::zero`].
|
/// See [`PerThing::zero`].
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
<Self as PerThing>::zero()
|
<Self as PerThing>::zero()
|
||||||
@@ -296,23 +442,63 @@ macro_rules! implement_per_thing {
|
|||||||
<Self as PerThing>::from_rational_approximation(p, q)
|
<Self as PerThing>::from_rational_approximation(p, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`PerThing::mul_collapse`].
|
/// See [`PerThing::mul_floor`].
|
||||||
pub fn mul_collapse<N>(self, b: N) -> N
|
pub fn mul_floor<N>(self, b: N) -> N
|
||||||
where N: Clone + From<$type> + UniqueSaturatedInto<$type> +
|
where N: Clone + From<$type> + UniqueSaturatedInto<$type> +
|
||||||
ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
|
ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
|
||||||
ops::Add<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 {
|
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 {
|
fn saturating_add(self, rhs: Self) -> Self {
|
||||||
// defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow.
|
// defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow.
|
||||||
Self::from_parts(self.0.saturating_add(rhs.0))
|
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 {
|
fn saturating_sub(self, rhs: Self) -> Self {
|
||||||
Self::from_parts(self.0.saturating_sub(rhs.0))
|
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 {
|
fn saturating_mul(self, rhs: Self) -> Self {
|
||||||
let a = self.0 as $upper_type;
|
let a = self.0 as $upper_type;
|
||||||
let b = rhs.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.
|
// This will always fit into $type.
|
||||||
Self::from_parts(parts as $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 {
|
impl crate::traits::Bounded for $name {
|
||||||
@@ -345,8 +556,7 @@ macro_rules! implement_per_thing {
|
|||||||
|
|
||||||
/// Non-overflow multiplication.
|
/// 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
|
impl<N> ops::Mul<N> for $name
|
||||||
where
|
where
|
||||||
N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N>
|
N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N>
|
||||||
@@ -354,37 +564,7 @@ macro_rules! implement_per_thing {
|
|||||||
{
|
{
|
||||||
type Output = N;
|
type Output = N;
|
||||||
fn mul(self, b: N) -> Self::Output {
|
fn mul(self, b: N) -> Self::Output {
|
||||||
let maximum: N = $max.into();
|
overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Nearest)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +590,9 @@ macro_rules! implement_per_thing {
|
|||||||
// for something like percent they can be the same.
|
// for something like percent they can be the same.
|
||||||
assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value());
|
assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value());
|
||||||
assert!(<$upper_type>::from($max).checked_mul($max.into()).is_some());
|
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)]
|
#[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::zero(), $name::from_parts(Zero::zero()));
|
||||||
assert_eq!($name::one(), $name::from_parts($max));
|
assert_eq!($name::one(), $name::from_parts($max));
|
||||||
assert_eq!($name::ACCURACY, $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.0), $name::from_parts(Zero::zero()));
|
||||||
assert_eq!($name::from_fraction(0.1), $name::from_parts($max / 10));
|
assert_eq!($name::from_fraction(0.1), $name::from_parts($max / 10));
|
||||||
assert_eq!($name::from_fraction(1.0), $name::from_parts($max));
|
assert_eq!($name::from_fraction(1.0), $name::from_parts($max));
|
||||||
@@ -742,6 +931,177 @@ macro_rules! implement_per_thing {
|
|||||||
2,
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use codec::HasCompact;
|
|||||||
pub use integer_sqrt::IntegerSquareRoot;
|
pub use integer_sqrt::IntegerSquareRoot;
|
||||||
pub use num_traits::{
|
pub use num_traits::{
|
||||||
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
|
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
|
||||||
CheckedShl, CheckedShr
|
CheckedShl, CheckedShr, checked_pow
|
||||||
};
|
};
|
||||||
use sp_std::ops::{
|
use sp_std::ops::{
|
||||||
Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign,
|
Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign,
|
||||||
@@ -104,29 +104,41 @@ impl<T: Bounded + Sized, S: TryInto<T> + Sized> UniqueSaturatedInto<T> for S {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple trait to use checked mul and max value to give a saturated mul operation over
|
/// Saturating arithmetic operations, returning maximum or minimum values instead of overflowing.
|
||||||
/// supported types.
|
|
||||||
pub trait Saturating {
|
pub trait Saturating {
|
||||||
/// Saturated addition - if the product can't fit in the type then just use max-value.
|
/// Saturating addition. Compute `self + rhs`, saturating at the numeric bounds instead of
|
||||||
fn saturating_add(self, o: Self) -> Self;
|
/// overflowing.
|
||||||
|
fn saturating_add(self, rhs: Self) -> Self;
|
||||||
|
|
||||||
/// Saturated subtraction - if the product can't fit in the type then just use max-value.
|
/// Saturating subtraction. Compute `self - rhs`, saturating at the numeric bounds instead of
|
||||||
fn saturating_sub(self, o: Self) -> Self;
|
/// overflowing.
|
||||||
|
fn saturating_sub(self, rhs: Self) -> Self;
|
||||||
|
|
||||||
/// Saturated multiply - if the product can't fit in the type then just use max-value.
|
/// Saturating multiply. Compute `self * rhs`, saturating at the numeric bounds instead of
|
||||||
fn saturating_mul(self, o: Self) -> Self;
|
/// overflowing.
|
||||||
|
fn saturating_mul(self, rhs: Self) -> Self;
|
||||||
|
|
||||||
|
/// Saturating exponentiation. Compute `self.pow(exp)`, saturating at the numeric bounds
|
||||||
|
/// instead of overflowing.
|
||||||
|
fn saturating_pow(self, exp: usize) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: CheckedMul + Bounded + num_traits::Saturating> Saturating for T {
|
impl<T: Clone + One + CheckedMul + Bounded + num_traits::Saturating> Saturating for T {
|
||||||
fn saturating_add(self, o: Self) -> Self {
|
fn saturating_add(self, o: Self) -> Self {
|
||||||
<Self as num_traits::Saturating>::saturating_add(self, o)
|
<Self as num_traits::Saturating>::saturating_add(self, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn saturating_sub(self, o: Self) -> Self {
|
fn saturating_sub(self, o: Self) -> Self {
|
||||||
<Self as num_traits::Saturating>::saturating_sub(self, o)
|
<Self as num_traits::Saturating>::saturating_sub(self, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn saturating_mul(self, o: Self) -> Self {
|
fn saturating_mul(self, o: Self) -> Self {
|
||||||
self.checked_mul(&o).unwrap_or_else(Bounded::max_value)
|
self.checked_mul(&o).unwrap_or_else(Bounded::max_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn saturating_pow(self, exp: usize) -> Self {
|
||||||
|
checked_pow(self, exp).unwrap_or_else(Bounded::max_value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience type to work around the highly unergonomic syntax needed
|
/// Convenience type to work around the highly unergonomic syntax needed
|
||||||
|
|||||||
Reference in New Issue
Block a user