mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 20:07:56 +00:00
0e49ed72aa
* add serde_full feature flag add serde_full to sp_runtime add space to toml add serde_full to application-crypto add serde_full to arithmetic fix arithmetic add serde full to beefy add serde full to consensus add serde_full to core add serdefull to finality grandpa add serde_full to several primitives crates make rpc no_std compatible add scale info to runtime make serializer no_std compatible add serde full to storage add full serde to version add serde full to weights add all serde_full features add . to comment add missing impl-serde fix no-std build fix build add full_crypto to serde_full serde_full also implements crypto full_serde does not work with full_crytpo. needs std no no_std serde impl possible also for crypto std is necessary no serde full for application crypto fix arithmetic fix tomls fix some things impl fmt for Signature add serialize to Public add impl_maybe_marker_serde_full fix sp-application-crypto toml add serde feature flag fix clippy fix toml grandpa fix grandpa rename if_std to if_serde keystore is not no_std compatible make keystore vrf no_std compatible fix nopos-elections fix rpc fix serializer fix test-primitives fix version add comment add serde full only import for format string remove all(serde_full and full_crypot) as serde_full enforces full_crypto make comment better readable even better comment clean up rpc toml clean up toml clean up serializer toml clean up storage toml fix std build update .lock fix sp-version move sp_std import test extern crate alloc replace sp_std with core add missing core sp_core: serde feature do not enforce full crypto application-crypto: serde feature do not enforce full crypto rename serde_full to serde add dep:serde and alloc to default feature add full_crypto and remove unnecessary debu/fmt impls for serde update comment remove obolsete change in display AccountId32 remove extra changes minimize diff revert keystore changes remove std from keystore remove full-crypto feature fix serde import fix comment fix feature = serde * rename serde_full to serde * move #[doc(hidden)] back * remove feature = full crypto require frm MultiSigner * reorder serde and scale_info import * fix bs58 missing alloc import in serde feature * add `from_string` to serde feature and add unimplemented * remove serde feature from fixed_point display * Remove serde/alloc Co-authored-by: Davide Galassi <davxy@datawok.net> * Update primitives/consensus/babe/Cargo.toml Co-authored-by: Bastian Köcher <git@kchr.de> * Update primitives/arithmetic/src/fixed_point.rs Co-authored-by: Bastian Köcher <git@kchr.de> * revert `from_string`fixed impl back to std only * remove duplicate runtime string impl * use sp_std::alloc * remove no_std compatible rpc * remove no_std compatibility from serializer * rename mpl_maybe_marker_serde to std_or_serde * update .lock * add sp-std to executor * fix sp-std import * fix sp_std::format import * use crate import * add serde feature * Update primitives/core/src/lib.rs --------- Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Bastian Köcher <git@kchr.de>
2215 lines
65 KiB
Rust
2215 lines
65 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Decimal Fixed Point implementations for Substrate runtime.
|
|
|
|
use crate::{
|
|
helpers_128bit::{multiply_by_rational_with_rounding, sqrt},
|
|
traits::{
|
|
Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedSub, One,
|
|
SaturatedConversion, Saturating, UniqueSaturatedInto, Zero,
|
|
},
|
|
PerThing, Perbill, Rounding, SignedRounding,
|
|
};
|
|
use codec::{CompactAs, Decode, Encode};
|
|
use sp_std::{
|
|
fmt::Debug,
|
|
ops::{self, Add, Div, Mul, Sub},
|
|
prelude::*,
|
|
};
|
|
|
|
#[cfg(feature = "serde")]
|
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
#[cfg(all(not(feature = "std"), feature = "serde"))]
|
|
use sp_std::alloc::string::{String, ToString};
|
|
|
|
/// Integer types that can be used to interact with `FixedPointNumber` implementations.
|
|
pub trait FixedPointOperand:
|
|
Copy
|
|
+ Clone
|
|
+ Bounded
|
|
+ Zero
|
|
+ Saturating
|
|
+ PartialOrd
|
|
+ UniqueSaturatedInto<u128>
|
|
+ TryFrom<u128>
|
|
+ CheckedNeg
|
|
{
|
|
}
|
|
|
|
impl FixedPointOperand for i128 {}
|
|
impl FixedPointOperand for u128 {}
|
|
impl FixedPointOperand for i64 {}
|
|
impl FixedPointOperand for u64 {}
|
|
impl FixedPointOperand for i32 {}
|
|
impl FixedPointOperand for u32 {}
|
|
impl FixedPointOperand for i16 {}
|
|
impl FixedPointOperand for u16 {}
|
|
impl FixedPointOperand for i8 {}
|
|
impl FixedPointOperand for u8 {}
|
|
|
|
/// Something that implements a decimal fixed point number.
|
|
///
|
|
/// The precision is given by `Self::DIV`, i.e. `1 / DIV` can be represented.
|
|
///
|
|
/// Each type can store numbers from `Self::Inner::min_value() / Self::DIV`
|
|
/// to `Self::Inner::max_value() / Self::DIV`.
|
|
/// This is also referred to as the _accuracy_ of the type in the documentation.
|
|
pub trait FixedPointNumber:
|
|
Sized
|
|
+ Copy
|
|
+ Default
|
|
+ Debug
|
|
+ Saturating
|
|
+ Bounded
|
|
+ Eq
|
|
+ PartialEq
|
|
+ Ord
|
|
+ PartialOrd
|
|
+ CheckedSub
|
|
+ CheckedAdd
|
|
+ CheckedMul
|
|
+ CheckedDiv
|
|
+ Add
|
|
+ Sub
|
|
+ Div
|
|
+ Mul
|
|
+ Zero
|
|
+ One
|
|
{
|
|
/// The underlying data type used for this fixed point number.
|
|
type Inner: Debug + One + CheckedMul + CheckedDiv + FixedPointOperand;
|
|
|
|
/// Precision of this fixed point implementation. It should be a power of `10`.
|
|
const DIV: Self::Inner;
|
|
|
|
/// Indicates if this fixed point implementation is signed or not.
|
|
const SIGNED: bool;
|
|
|
|
/// Precision of this fixed point implementation.
|
|
fn accuracy() -> Self::Inner {
|
|
Self::DIV
|
|
}
|
|
|
|
/// Builds this type from an integer number.
|
|
fn from_inner(int: Self::Inner) -> Self;
|
|
|
|
/// Consumes `self` and returns the inner raw value.
|
|
fn into_inner(self) -> Self::Inner;
|
|
|
|
/// Creates self from an integer number `int`.
|
|
///
|
|
/// Returns `Self::max` or `Self::min` if `int` exceeds accuracy.
|
|
fn saturating_from_integer<N: FixedPointOperand>(int: N) -> Self {
|
|
let mut n: I129 = int.into();
|
|
n.value = n.value.saturating_mul(Self::DIV.saturated_into());
|
|
Self::from_inner(from_i129(n).unwrap_or_else(|| to_bound(int, 0)))
|
|
}
|
|
|
|
/// Creates `self` from an integer number `int`.
|
|
///
|
|
/// Returns `None` if `int` exceeds accuracy.
|
|
fn checked_from_integer<N: Into<Self::Inner>>(int: N) -> Option<Self> {
|
|
let int: Self::Inner = int.into();
|
|
int.checked_mul(&Self::DIV).map(Self::from_inner)
|
|
}
|
|
|
|
/// Creates `self` from a rational number. Equal to `n / d`.
|
|
///
|
|
/// Panics if `d = 0`. Returns `Self::max` or `Self::min` if `n / d` exceeds accuracy.
|
|
fn saturating_from_rational<N: FixedPointOperand, D: FixedPointOperand>(n: N, d: D) -> Self {
|
|
if d == D::zero() {
|
|
panic!("attempt to divide by zero")
|
|
}
|
|
Self::checked_from_rational(n, d).unwrap_or_else(|| to_bound(n, d))
|
|
}
|
|
|
|
/// Creates `self` from a rational number. Equal to `n / d`.
|
|
///
|
|
/// Returns `None` if `d == 0` or `n / d` exceeds accuracy.
|
|
fn checked_from_rational<N: FixedPointOperand, D: FixedPointOperand>(
|
|
n: N,
|
|
d: D,
|
|
) -> Option<Self> {
|
|
if d == D::zero() {
|
|
return None
|
|
}
|
|
|
|
let n: I129 = n.into();
|
|
let d: I129 = d.into();
|
|
let negative = n.negative != d.negative;
|
|
|
|
multiply_by_rational_with_rounding(
|
|
n.value,
|
|
Self::DIV.unique_saturated_into(),
|
|
d.value,
|
|
Rounding::from_signed(SignedRounding::Minor, negative),
|
|
)
|
|
.and_then(|value| from_i129(I129 { value, negative }))
|
|
.map(Self::from_inner)
|
|
}
|
|
|
|
/// Checked multiplication for integer type `N`. Equal to `self * n`.
|
|
///
|
|
/// Returns `None` if the result does not fit in `N`.
|
|
fn checked_mul_int<N: FixedPointOperand>(self, n: N) -> Option<N> {
|
|
let lhs: I129 = self.into_inner().into();
|
|
let rhs: I129 = n.into();
|
|
let negative = lhs.negative != rhs.negative;
|
|
|
|
multiply_by_rational_with_rounding(
|
|
lhs.value,
|
|
rhs.value,
|
|
Self::DIV.unique_saturated_into(),
|
|
Rounding::from_signed(SignedRounding::Minor, negative),
|
|
)
|
|
.and_then(|value| from_i129(I129 { value, negative }))
|
|
}
|
|
|
|
/// Saturating multiplication for integer type `N`. Equal to `self * n`.
|
|
///
|
|
/// Returns `N::min` or `N::max` if the result does not fit in `N`.
|
|
fn saturating_mul_int<N: FixedPointOperand>(self, n: N) -> N {
|
|
self.checked_mul_int(n).unwrap_or_else(|| to_bound(self.into_inner(), n))
|
|
}
|
|
|
|
/// Checked division for integer type `N`. Equal to `self / d`.
|
|
///
|
|
/// Returns `None` if the result does not fit in `N` or `d == 0`.
|
|
fn checked_div_int<N: FixedPointOperand>(self, d: N) -> Option<N> {
|
|
let lhs: I129 = self.into_inner().into();
|
|
let rhs: I129 = d.into();
|
|
let negative = lhs.negative != rhs.negative;
|
|
|
|
lhs.value
|
|
.checked_div(rhs.value)
|
|
.and_then(|n| n.checked_div(Self::DIV.unique_saturated_into()))
|
|
.and_then(|value| from_i129(I129 { value, negative }))
|
|
}
|
|
|
|
/// Saturating division for integer type `N`. Equal to `self / d`.
|
|
///
|
|
/// Panics if `d == 0`. Returns `N::min` or `N::max` if the result does not fit in `N`.
|
|
fn saturating_div_int<N: FixedPointOperand>(self, d: N) -> N {
|
|
if d == N::zero() {
|
|
panic!("attempt to divide by zero")
|
|
}
|
|
self.checked_div_int(d).unwrap_or_else(|| to_bound(self.into_inner(), d))
|
|
}
|
|
|
|
/// Saturating multiplication for integer type `N`, adding the result back.
|
|
/// Equal to `self * n + n`.
|
|
///
|
|
/// Returns `N::min` or `N::max` if the multiplication or final result does not fit in `N`.
|
|
fn saturating_mul_acc_int<N: FixedPointOperand>(self, n: N) -> N {
|
|
if self.is_negative() && n > N::zero() {
|
|
n.saturating_sub(Self::zero().saturating_sub(self).saturating_mul_int(n))
|
|
} else {
|
|
self.saturating_mul_int(n).saturating_add(n)
|
|
}
|
|
}
|
|
|
|
/// Saturating absolute value.
|
|
///
|
|
/// Returns `Self::max` if `self == Self::min`.
|
|
fn saturating_abs(self) -> Self {
|
|
let inner = self.into_inner();
|
|
if inner >= Self::Inner::zero() {
|
|
self
|
|
} else {
|
|
Self::from_inner(inner.checked_neg().unwrap_or_else(Self::Inner::max_value))
|
|
}
|
|
}
|
|
|
|
/// Takes the reciprocal (inverse). Equal to `1 / self`.
|
|
///
|
|
/// Returns `None` if `self = 0`.
|
|
fn reciprocal(self) -> Option<Self> {
|
|
Self::one().checked_div(&self)
|
|
}
|
|
|
|
/// Checks if the number is one.
|
|
fn is_one(&self) -> bool {
|
|
self.into_inner() == Self::Inner::one()
|
|
}
|
|
|
|
/// Returns `true` if `self` is positive and `false` if the number is zero or negative.
|
|
fn is_positive(self) -> bool {
|
|
self.into_inner() > Self::Inner::zero()
|
|
}
|
|
|
|
/// Returns `true` if `self` is negative and `false` if the number is zero or positive.
|
|
fn is_negative(self) -> bool {
|
|
self.into_inner() < Self::Inner::zero()
|
|
}
|
|
|
|
/// Returns the integer part.
|
|
fn trunc(self) -> Self {
|
|
self.into_inner()
|
|
.checked_div(&Self::DIV)
|
|
.expect("panics only if DIV is zero, DIV is not zero; qed")
|
|
.checked_mul(&Self::DIV)
|
|
.map(Self::from_inner)
|
|
.expect("can not overflow since fixed number is >= integer part")
|
|
}
|
|
|
|
/// Returns the fractional part.
|
|
///
|
|
/// Note: the returned fraction will be non-negative for negative numbers,
|
|
/// except in the case where the integer part is zero.
|
|
fn frac(self) -> Self {
|
|
let integer = self.trunc();
|
|
let fractional = self.saturating_sub(integer);
|
|
if integer == Self::zero() {
|
|
fractional
|
|
} else {
|
|
fractional.saturating_abs()
|
|
}
|
|
}
|
|
|
|
/// Returns the smallest integer greater than or equal to a number.
|
|
///
|
|
/// Saturates to `Self::max` (truncated) if the result does not fit.
|
|
fn ceil(self) -> Self {
|
|
if self.is_negative() {
|
|
self.trunc()
|
|
} else if self.frac() == Self::zero() {
|
|
self
|
|
} else {
|
|
self.saturating_add(Self::one()).trunc()
|
|
}
|
|
}
|
|
|
|
/// Returns the largest integer less than or equal to a number.
|
|
///
|
|
/// Saturates to `Self::min` (truncated) if the result does not fit.
|
|
fn floor(self) -> Self {
|
|
if self.is_negative() {
|
|
self.saturating_sub(Self::one()).trunc()
|
|
} else {
|
|
self.trunc()
|
|
}
|
|
}
|
|
|
|
/// Returns the number rounded to the nearest integer. Rounds half-way cases away from 0.0.
|
|
///
|
|
/// Saturates to `Self::min` or `Self::max` (truncated) if the result does not fit.
|
|
fn round(self) -> Self {
|
|
let n = self.frac().saturating_mul(Self::saturating_from_integer(10));
|
|
if n < Self::saturating_from_integer(5) {
|
|
self.trunc()
|
|
} else if self.is_positive() {
|
|
self.saturating_add(Self::one()).trunc()
|
|
} else {
|
|
self.saturating_sub(Self::one()).trunc()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Data type used as intermediate storage in some computations to avoid overflow.
|
|
struct I129 {
|
|
value: u128,
|
|
negative: bool,
|
|
}
|
|
|
|
impl<N: FixedPointOperand> From<N> for I129 {
|
|
fn from(n: N) -> I129 {
|
|
if n < N::zero() {
|
|
let value: u128 = n
|
|
.checked_neg()
|
|
.map(|n| n.unique_saturated_into())
|
|
.unwrap_or_else(|| N::max_value().unique_saturated_into().saturating_add(1));
|
|
I129 { value, negative: true }
|
|
} else {
|
|
I129 { value: n.unique_saturated_into(), negative: false }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Transforms an `I129` to `N` if it is possible.
|
|
fn from_i129<N: FixedPointOperand>(n: I129) -> Option<N> {
|
|
let max_plus_one: u128 = N::max_value().unique_saturated_into().saturating_add(1);
|
|
if n.negative && N::min_value() < N::zero() && n.value == max_plus_one {
|
|
Some(N::min_value())
|
|
} else {
|
|
let unsigned_inner: N = n.value.try_into().ok()?;
|
|
let inner = if n.negative { unsigned_inner.checked_neg()? } else { unsigned_inner };
|
|
Some(inner)
|
|
}
|
|
}
|
|
|
|
/// Returns `R::max` if the sign of `n * m` is positive, `R::min` otherwise.
|
|
fn to_bound<N: FixedPointOperand, D: FixedPointOperand, R: Bounded>(n: N, m: D) -> R {
|
|
if (n < N::zero()) != (m < D::zero()) {
|
|
R::min_value()
|
|
} else {
|
|
R::max_value()
|
|
}
|
|
}
|
|
|
|
macro_rules! implement_fixed {
|
|
(
|
|
$name:ident,
|
|
$test_mod:ident,
|
|
$inner_type:ty,
|
|
$signed:tt,
|
|
$div:tt,
|
|
$title:expr $(,)?
|
|
) => {
|
|
/// A fixed point number representation in the range.
|
|
#[doc = $title]
|
|
#[derive(
|
|
Encode,
|
|
Decode,
|
|
CompactAs,
|
|
Default,
|
|
Copy,
|
|
Clone,
|
|
codec::MaxEncodedLen,
|
|
PartialEq,
|
|
Eq,
|
|
PartialOrd,
|
|
Ord,
|
|
scale_info::TypeInfo,
|
|
)]
|
|
pub struct $name($inner_type);
|
|
|
|
impl From<$inner_type> for $name {
|
|
fn from(int: $inner_type) -> Self {
|
|
$name::saturating_from_integer(int)
|
|
}
|
|
}
|
|
|
|
impl<N: FixedPointOperand, D: FixedPointOperand> From<(N, D)> for $name {
|
|
fn from(r: (N, D)) -> Self {
|
|
$name::saturating_from_rational(r.0, r.1)
|
|
}
|
|
}
|
|
|
|
impl FixedPointNumber for $name {
|
|
type Inner = $inner_type;
|
|
|
|
const DIV: Self::Inner = $div;
|
|
const SIGNED: bool = $signed;
|
|
|
|
fn from_inner(inner: Self::Inner) -> Self {
|
|
Self(inner)
|
|
}
|
|
|
|
fn into_inner(self) -> Self::Inner {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl $name {
|
|
/// Create a new instance from the given `inner` value.
|
|
///
|
|
/// `const` version of `FixedPointNumber::from_inner`.
|
|
pub const fn from_inner(inner: $inner_type) -> Self {
|
|
Self(inner)
|
|
}
|
|
|
|
/// Return the instance's inner value.
|
|
///
|
|
/// `const` version of `FixedPointNumber::into_inner`.
|
|
pub const fn into_inner(self) -> $inner_type {
|
|
self.0
|
|
}
|
|
|
|
/// Creates self from a `u32`.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn from_u32(n: u32) -> Self {
|
|
Self::from_inner((n as $inner_type) * $div)
|
|
}
|
|
|
|
/// Convert from a `float` value.
|
|
#[cfg(any(feature = "std", test))]
|
|
pub fn from_float(x: f64) -> Self {
|
|
Self((x * (<Self as FixedPointNumber>::DIV as f64)) as $inner_type)
|
|
}
|
|
|
|
/// Convert from a `Perbill` value.
|
|
pub const fn from_perbill(n: Perbill) -> Self {
|
|
Self::from_rational(n.deconstruct() as u128, 1_000_000_000)
|
|
}
|
|
|
|
/// Convert into a `Perbill` value. Will saturate if above one or below zero.
|
|
pub const fn into_perbill(self) -> Perbill {
|
|
if self.0 <= 0 {
|
|
Perbill::zero()
|
|
} else if self.0 >= $div {
|
|
Perbill::one()
|
|
} else {
|
|
match multiply_by_rational_with_rounding(
|
|
self.0 as u128,
|
|
1_000_000_000,
|
|
Self::DIV as u128,
|
|
Rounding::NearestPrefDown,
|
|
) {
|
|
Some(value) => {
|
|
if value > (u32::max_value() as u128) {
|
|
panic!(
|
|
"prior logic ensures 0<self.0<DIV; \
|
|
multiply ensures 0<self.0<1000000000; \
|
|
qed"
|
|
);
|
|
}
|
|
Perbill::from_parts(value as u32)
|
|
},
|
|
None => Perbill::zero(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Convert into a `float` value.
|
|
#[cfg(any(feature = "std", test))]
|
|
pub fn to_float(self) -> f64 {
|
|
self.0 as f64 / <Self as FixedPointNumber>::DIV as f64
|
|
}
|
|
|
|
/// Attempt to convert into a `PerThing`. This will succeed iff `self` is at least zero
|
|
/// and at most one. If it is out of bounds, it will result in an error returning the
|
|
/// clamped value.
|
|
pub fn try_into_perthing<P: PerThing>(self) -> Result<P, P> {
|
|
if self < Self::zero() {
|
|
Err(P::zero())
|
|
} else if self > Self::one() {
|
|
Err(P::one())
|
|
} else {
|
|
Ok(P::from_rational(self.0 as u128, $div))
|
|
}
|
|
}
|
|
|
|
/// Attempt to convert into a `PerThing`. This will always succeed resulting in a
|
|
/// clamped value if `self` is less than zero or greater than one.
|
|
pub fn into_clamped_perthing<P: PerThing>(self) -> P {
|
|
if self < Self::zero() {
|
|
P::zero()
|
|
} else if self > Self::one() {
|
|
P::one()
|
|
} else {
|
|
P::from_rational(self.0 as u128, $div)
|
|
}
|
|
}
|
|
|
|
/// Negate the value.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn neg(self) -> Self {
|
|
Self(0 - self.0)
|
|
}
|
|
|
|
/// Take the square root of a positive value.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn sqrt(self) -> Self {
|
|
match self.try_sqrt() {
|
|
Some(v) => v,
|
|
None => panic!("sqrt overflow or negative input"),
|
|
}
|
|
}
|
|
|
|
/// Compute the square root, rounding as desired. If it overflows or is negative, then
|
|
/// `None` is returned.
|
|
pub const fn try_sqrt(self) -> Option<Self> {
|
|
if self.0 == 0 {
|
|
return Some(Self(0))
|
|
}
|
|
if self.0 < 1 {
|
|
return None
|
|
}
|
|
let v = self.0 as u128;
|
|
|
|
// Want x' = sqrt(x) where x = n/D and x' = n'/D (D is fixed)
|
|
// Our prefered way is:
|
|
// sqrt(n/D) = sqrt(nD / D^2) = sqrt(nD)/sqrt(D^2) = sqrt(nD)/D
|
|
// ergo n' = sqrt(nD)
|
|
// but this requires nD to fit into our type.
|
|
// if nD doesn't fit then we can fall back on:
|
|
// sqrt(nD) = sqrt(n)*sqrt(D)
|
|
// computing them individually and taking the product at the end. we will lose some
|
|
// precision though.
|
|
let maybe_vd = u128::checked_mul(v, $div);
|
|
let r = if let Some(vd) = maybe_vd { sqrt(vd) } else { sqrt(v) * sqrt($div) };
|
|
Some(Self(r as $inner_type))
|
|
}
|
|
|
|
/// Add a value and return the result.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn add(self, rhs: Self) -> Self {
|
|
Self(self.0 + rhs.0)
|
|
}
|
|
|
|
/// Subtract a value and return the result.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn sub(self, rhs: Self) -> Self {
|
|
Self(self.0 - rhs.0)
|
|
}
|
|
|
|
/// Multiply by a value and return the result.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn mul(self, rhs: Self) -> Self {
|
|
match $name::const_checked_mul(self, rhs) {
|
|
Some(v) => v,
|
|
None => panic!("attempt to multiply with overflow"),
|
|
}
|
|
}
|
|
|
|
/// Divide by a value and return the result.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn div(self, rhs: Self) -> Self {
|
|
match $name::const_checked_div(self, rhs) {
|
|
Some(v) => v,
|
|
None => panic!("attempt to divide with overflow or NaN"),
|
|
}
|
|
}
|
|
|
|
/// Convert into an `I129` format value.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
const fn into_i129(self) -> I129 {
|
|
#[allow(unused_comparisons)]
|
|
if self.0 < 0 {
|
|
let value = match self.0.checked_neg() {
|
|
Some(n) => n as u128,
|
|
None => u128::saturating_add(<$inner_type>::max_value() as u128, 1),
|
|
};
|
|
I129 { value, negative: true }
|
|
} else {
|
|
I129 { value: self.0 as u128, negative: false }
|
|
}
|
|
}
|
|
|
|
/// Convert from an `I129` format value.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
const fn from_i129(n: I129) -> Option<Self> {
|
|
let max_plus_one = u128::saturating_add(<$inner_type>::max_value() as u128, 1);
|
|
#[allow(unused_comparisons)]
|
|
let inner = if n.negative && <$inner_type>::min_value() < 0 && n.value == max_plus_one {
|
|
<$inner_type>::min_value()
|
|
} else {
|
|
let unsigned_inner = n.value as $inner_type;
|
|
if unsigned_inner as u128 != n.value || (unsigned_inner > 0) != (n.value > 0) {
|
|
return None
|
|
};
|
|
if n.negative {
|
|
match unsigned_inner.checked_neg() {
|
|
Some(v) => v,
|
|
None => return None,
|
|
}
|
|
} else {
|
|
unsigned_inner
|
|
}
|
|
};
|
|
Some(Self(inner))
|
|
}
|
|
|
|
/// Calculate an approximation of a rational.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn from_rational(a: u128, b: u128) -> Self {
|
|
Self::from_rational_with_rounding(a, b, Rounding::NearestPrefDown)
|
|
}
|
|
|
|
/// Calculate an approximation of a rational with custom rounding.
|
|
///
|
|
/// WARNING: This is a `const` function designed for convenient use at build time and
|
|
/// will panic on overflow. Ensure that any inputs are sensible.
|
|
pub const fn from_rational_with_rounding(a: u128, b: u128, rounding: Rounding) -> Self {
|
|
if b == 0 {
|
|
panic!("attempt to divide by zero in from_rational")
|
|
}
|
|
match multiply_by_rational_with_rounding(Self::DIV as u128, a, b, rounding) {
|
|
Some(value) => match Self::from_i129(I129 { value, negative: false }) {
|
|
Some(x) => x,
|
|
None => panic!("overflow in from_rational"),
|
|
},
|
|
None => panic!("overflow in from_rational"),
|
|
}
|
|
}
|
|
|
|
/// Multiply by another value, returning `None` in the case of an error.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
pub const fn const_checked_mul(self, other: Self) -> Option<Self> {
|
|
self.const_checked_mul_with_rounding(other, SignedRounding::NearestPrefLow)
|
|
}
|
|
|
|
/// Multiply by another value with custom rounding, returning `None` in the case of an
|
|
/// error.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
pub const fn const_checked_mul_with_rounding(
|
|
self,
|
|
other: Self,
|
|
rounding: SignedRounding,
|
|
) -> Option<Self> {
|
|
let lhs = self.into_i129();
|
|
let rhs = other.into_i129();
|
|
let negative = lhs.negative != rhs.negative;
|
|
|
|
match multiply_by_rational_with_rounding(
|
|
lhs.value,
|
|
rhs.value,
|
|
Self::DIV as u128,
|
|
Rounding::from_signed(rounding, negative),
|
|
) {
|
|
Some(value) => Self::from_i129(I129 { value, negative }),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
/// Divide by another value, returning `None` in the case of an error.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
pub const fn const_checked_div(self, other: Self) -> Option<Self> {
|
|
self.checked_rounding_div(other, SignedRounding::NearestPrefLow)
|
|
}
|
|
|
|
/// Divide by another value with custom rounding, returning `None` in the case of an
|
|
/// error.
|
|
///
|
|
/// Result will be rounded to the nearest representable value, rounding down if it is
|
|
/// equidistant between two neighbours.
|
|
pub const fn checked_rounding_div(
|
|
self,
|
|
other: Self,
|
|
rounding: SignedRounding,
|
|
) -> Option<Self> {
|
|
if other.0 == 0 {
|
|
return None
|
|
}
|
|
|
|
let lhs = self.into_i129();
|
|
let rhs = other.into_i129();
|
|
let negative = lhs.negative != rhs.negative;
|
|
|
|
match multiply_by_rational_with_rounding(
|
|
lhs.value,
|
|
Self::DIV as u128,
|
|
rhs.value,
|
|
Rounding::from_signed(rounding, negative),
|
|
) {
|
|
Some(value) => Self::from_i129(I129 { value, negative }),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Saturating for $name {
|
|
fn saturating_add(self, rhs: Self) -> Self {
|
|
Self(self.0.saturating_add(rhs.0))
|
|
}
|
|
|
|
fn saturating_sub(self, rhs: Self) -> Self {
|
|
Self(self.0.saturating_sub(rhs.0))
|
|
}
|
|
|
|
fn saturating_mul(self, rhs: Self) -> Self {
|
|
self.checked_mul(&rhs).unwrap_or_else(|| to_bound(self.0, rhs.0))
|
|
}
|
|
|
|
fn saturating_pow(self, exp: usize) -> Self {
|
|
if exp == 0 {
|
|
return Self::saturating_from_integer(1)
|
|
}
|
|
|
|
let exp = exp as u32;
|
|
let msb_pos = 32 - exp.leading_zeros();
|
|
|
|
let mut result = Self::saturating_from_integer(1);
|
|
let mut pow_val = self;
|
|
for i in 0..msb_pos {
|
|
if ((1 << i) & exp) > 0 {
|
|
result = result.saturating_mul(pow_val);
|
|
}
|
|
pow_val = pow_val.saturating_mul(pow_val);
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
impl ops::Neg for $name {
|
|
type Output = Self;
|
|
|
|
fn neg(self) -> Self::Output {
|
|
Self(<Self as FixedPointNumber>::Inner::zero() - self.0)
|
|
}
|
|
}
|
|
|
|
impl ops::Add for $name {
|
|
type Output = Self;
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
Self(self.0 + rhs.0)
|
|
}
|
|
}
|
|
|
|
impl ops::Sub for $name {
|
|
type Output = Self;
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
Self(self.0 - rhs.0)
|
|
}
|
|
}
|
|
|
|
impl ops::Mul for $name {
|
|
type Output = Self;
|
|
|
|
fn mul(self, rhs: Self) -> Self::Output {
|
|
self.checked_mul(&rhs)
|
|
.unwrap_or_else(|| panic!("attempt to multiply with overflow"))
|
|
}
|
|
}
|
|
|
|
impl ops::Div for $name {
|
|
type Output = Self;
|
|
|
|
fn div(self, rhs: Self) -> Self::Output {
|
|
if rhs.0 == 0 {
|
|
panic!("attempt to divide by zero")
|
|
}
|
|
self.checked_div(&rhs)
|
|
.unwrap_or_else(|| panic!("attempt to divide with overflow"))
|
|
}
|
|
}
|
|
|
|
impl CheckedSub for $name {
|
|
fn checked_sub(&self, rhs: &Self) -> Option<Self> {
|
|
self.0.checked_sub(rhs.0).map(Self)
|
|
}
|
|
}
|
|
|
|
impl CheckedAdd for $name {
|
|
fn checked_add(&self, rhs: &Self) -> Option<Self> {
|
|
self.0.checked_add(rhs.0).map(Self)
|
|
}
|
|
}
|
|
|
|
impl CheckedDiv for $name {
|
|
fn checked_div(&self, other: &Self) -> Option<Self> {
|
|
if other.0 == 0 {
|
|
return None
|
|
}
|
|
|
|
let lhs: I129 = self.0.into();
|
|
let rhs: I129 = other.0.into();
|
|
let negative = lhs.negative != rhs.negative;
|
|
|
|
// Note that this uses the old (well-tested) code with sign-ignorant rounding. This
|
|
// is equivalent to the `SignedRounding::NearestPrefMinor`. This means it is
|
|
// expected to give exactly the same result as `const_checked_div` when the result
|
|
// is positive and a result up to one epsilon greater when it is negative.
|
|
multiply_by_rational_with_rounding(
|
|
lhs.value,
|
|
Self::DIV as u128,
|
|
rhs.value,
|
|
Rounding::from_signed(SignedRounding::Minor, negative),
|
|
)
|
|
.and_then(|value| from_i129(I129 { value, negative }))
|
|
.map(Self)
|
|
}
|
|
}
|
|
|
|
impl CheckedMul for $name {
|
|
fn checked_mul(&self, other: &Self) -> Option<Self> {
|
|
let lhs: I129 = self.0.into();
|
|
let rhs: I129 = other.0.into();
|
|
let negative = lhs.negative != rhs.negative;
|
|
|
|
multiply_by_rational_with_rounding(
|
|
lhs.value,
|
|
rhs.value,
|
|
Self::DIV as u128,
|
|
Rounding::from_signed(SignedRounding::Minor, negative),
|
|
)
|
|
.and_then(|value| from_i129(I129 { value, negative }))
|
|
.map(Self)
|
|
}
|
|
}
|
|
|
|
impl Bounded for $name {
|
|
fn min_value() -> Self {
|
|
Self(<Self as FixedPointNumber>::Inner::min_value())
|
|
}
|
|
|
|
fn max_value() -> Self {
|
|
Self(<Self as FixedPointNumber>::Inner::max_value())
|
|
}
|
|
}
|
|
|
|
impl Zero for $name {
|
|
fn zero() -> Self {
|
|
Self::from_inner(<Self as FixedPointNumber>::Inner::zero())
|
|
}
|
|
|
|
fn is_zero(&self) -> bool {
|
|
self.into_inner() == <Self as FixedPointNumber>::Inner::zero()
|
|
}
|
|
}
|
|
|
|
impl One for $name {
|
|
fn one() -> Self {
|
|
Self::from_inner(Self::DIV)
|
|
}
|
|
}
|
|
|
|
impl sp_std::fmt::Debug for $name {
|
|
#[cfg(feature = "std")]
|
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
let integral = {
|
|
let int = self.0 / Self::accuracy();
|
|
let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" };
|
|
format!("{}{}", signum_for_zero, int)
|
|
};
|
|
let precision = (Self::accuracy() as f64).log10() as usize;
|
|
let fractional = format!(
|
|
"{:0>weight$}",
|
|
((self.0 % Self::accuracy()) as i128).abs(),
|
|
weight = precision
|
|
);
|
|
write!(f, "{}({}.{})", stringify!($name), integral, fractional)
|
|
}
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<P: PerThing> From<P> for $name
|
|
where
|
|
P::Inner: FixedPointOperand,
|
|
{
|
|
fn from(p: P) -> Self {
|
|
let accuracy = P::ACCURACY;
|
|
let value = p.deconstruct();
|
|
$name::saturating_from_rational(value, accuracy)
|
|
}
|
|
}
|
|
|
|
impl sp_std::fmt::Display for $name {
|
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl sp_std::str::FromStr for $name {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let inner: <Self as FixedPointNumber>::Inner =
|
|
s.parse().map_err(|_| "invalid string input for fixed point number")?;
|
|
Ok(Self::from_inner(inner))
|
|
}
|
|
}
|
|
|
|
// Manual impl `Serialize` as serde_json does not support i128.
|
|
// TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed.
|
|
#[cfg(feature = "serde")]
|
|
impl Serialize for $name {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.serialize_str(&self.to_string())
|
|
}
|
|
}
|
|
|
|
// Manual impl `Deserialize` as serde_json does not support i128.
|
|
// TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed.
|
|
#[cfg(feature = "serde")]
|
|
impl<'de> Deserialize<'de> for $name {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
use sp_std::str::FromStr;
|
|
let s = String::deserialize(deserializer)?;
|
|
$name::from_str(&s).map_err(de::Error::custom)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod $test_mod {
|
|
use super::*;
|
|
use crate::{Perbill, Percent, Permill, Perquintill};
|
|
|
|
fn max() -> $name {
|
|
$name::max_value()
|
|
}
|
|
|
|
fn min() -> $name {
|
|
$name::min_value()
|
|
}
|
|
|
|
fn precision() -> usize {
|
|
($name::accuracy() as f64).log10() as usize
|
|
}
|
|
|
|
#[test]
|
|
fn macro_preconditions() {
|
|
assert!($name::DIV > 0);
|
|
}
|
|
|
|
#[test]
|
|
fn has_max_encoded_len() {
|
|
struct AsMaxEncodedLen<T: codec::MaxEncodedLen> {
|
|
_data: T,
|
|
}
|
|
|
|
let _ = AsMaxEncodedLen { _data: $name::min_value() };
|
|
}
|
|
|
|
#[test]
|
|
fn from_i129_works() {
|
|
let a = I129 { value: 1, negative: true };
|
|
|
|
// Can't convert negative number to unsigned.
|
|
assert_eq!(from_i129::<u128>(a), None);
|
|
|
|
let a = I129 { value: u128::MAX - 1, negative: false };
|
|
|
|
// Max - 1 value fits.
|
|
assert_eq!(from_i129::<u128>(a), Some(u128::MAX - 1));
|
|
|
|
let a = I129 { value: u128::MAX, negative: false };
|
|
|
|
// Max value fits.
|
|
assert_eq!(from_i129::<u128>(a), Some(u128::MAX));
|
|
|
|
let a = I129 { value: i128::MAX as u128 + 1, negative: true };
|
|
|
|
// Min value fits.
|
|
assert_eq!(from_i129::<i128>(a), Some(i128::MIN));
|
|
|
|
let a = I129 { value: i128::MAX as u128 + 1, negative: false };
|
|
|
|
// Max + 1 does not fit.
|
|
assert_eq!(from_i129::<i128>(a), None);
|
|
|
|
let a = I129 { value: i128::MAX as u128, negative: false };
|
|
|
|
// Max value fits.
|
|
assert_eq!(from_i129::<i128>(a), Some(i128::MAX));
|
|
}
|
|
|
|
#[test]
|
|
fn to_bound_works() {
|
|
let a = 1i32;
|
|
let b = 1i32;
|
|
|
|
// Pos + Pos => Max.
|
|
assert_eq!(to_bound::<_, _, i32>(a, b), i32::MAX);
|
|
|
|
let a = -1i32;
|
|
let b = -1i32;
|
|
|
|
// Neg + Neg => Max.
|
|
assert_eq!(to_bound::<_, _, i32>(a, b), i32::MAX);
|
|
|
|
let a = 1i32;
|
|
let b = -1i32;
|
|
|
|
// Pos + Neg => Min.
|
|
assert_eq!(to_bound::<_, _, i32>(a, b), i32::MIN);
|
|
|
|
let a = -1i32;
|
|
let b = 1i32;
|
|
|
|
// Neg + Pos => Min.
|
|
assert_eq!(to_bound::<_, _, i32>(a, b), i32::MIN);
|
|
|
|
let a = 1i32;
|
|
let b = -1i32;
|
|
|
|
// Pos + Neg => Min (unsigned).
|
|
assert_eq!(to_bound::<_, _, u32>(a, b), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn op_neg_works() {
|
|
let a = $name::zero();
|
|
let b = -a;
|
|
|
|
// Zero.
|
|
assert_eq!(a, b);
|
|
|
|
if $name::SIGNED {
|
|
let a = $name::saturating_from_integer(5);
|
|
let b = -a;
|
|
|
|
// Positive.
|
|
assert_eq!($name::saturating_from_integer(-5), b);
|
|
|
|
let a = $name::saturating_from_integer(-5);
|
|
let b = -a;
|
|
|
|
// Negative
|
|
assert_eq!($name::saturating_from_integer(5), b);
|
|
|
|
let a = $name::max_value();
|
|
let b = -a;
|
|
|
|
// Max.
|
|
assert_eq!($name::min_value() + $name::from_inner(1), b);
|
|
|
|
let a = $name::min_value() + $name::from_inner(1);
|
|
let b = -a;
|
|
|
|
// Min.
|
|
assert_eq!($name::max_value(), b);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn op_checked_add_overflow_works() {
|
|
let a = $name::max_value();
|
|
let b = 1.into();
|
|
assert!(a.checked_add(&b).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn op_add_works() {
|
|
let a = $name::saturating_from_rational(5, 2);
|
|
let b = $name::saturating_from_rational(1, 2);
|
|
|
|
// Positive case: 6/2 = 3.
|
|
assert_eq!($name::saturating_from_integer(3), a + b);
|
|
|
|
if $name::SIGNED {
|
|
// Negative case: 4/2 = 2.
|
|
let b = $name::saturating_from_rational(1, -2);
|
|
assert_eq!($name::saturating_from_integer(2), a + b);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn op_checked_sub_underflow_works() {
|
|
let a = $name::min_value();
|
|
let b = 1.into();
|
|
assert!(a.checked_sub(&b).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn op_sub_works() {
|
|
let a = $name::saturating_from_rational(5, 2);
|
|
let b = $name::saturating_from_rational(1, 2);
|
|
|
|
assert_eq!($name::saturating_from_integer(2), a - b);
|
|
assert_eq!($name::saturating_from_integer(-2), b.saturating_sub(a));
|
|
}
|
|
|
|
#[test]
|
|
fn op_checked_mul_overflow_works() {
|
|
let a = $name::max_value();
|
|
let b = 2.into();
|
|
assert!(a.checked_mul(&b).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn op_mul_works() {
|
|
let a = $name::saturating_from_integer(42);
|
|
let b = $name::saturating_from_integer(2);
|
|
assert_eq!($name::saturating_from_integer(84), a * b);
|
|
|
|
let a = $name::saturating_from_integer(42);
|
|
let b = $name::saturating_from_integer(-2);
|
|
assert_eq!($name::saturating_from_integer(-84), a * b);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "attempt to divide by zero")]
|
|
fn op_div_panics_on_zero_divisor() {
|
|
let a = $name::saturating_from_integer(1);
|
|
let b = 0.into();
|
|
let _c = a / b;
|
|
}
|
|
|
|
#[test]
|
|
fn op_checked_div_overflow_works() {
|
|
if $name::SIGNED {
|
|
let a = $name::min_value();
|
|
let b = $name::zero().saturating_sub($name::one());
|
|
assert!(a.checked_div(&b).is_none());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn op_sqrt_works() {
|
|
for i in 1..1_000i64 {
|
|
let x = $name::saturating_from_rational(i, 1_000i64);
|
|
assert_eq!((x * x).try_sqrt(), Some(x));
|
|
let x = $name::saturating_from_rational(i, 1i64);
|
|
assert_eq!((x * x).try_sqrt(), Some(x));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn op_div_works() {
|
|
let a = $name::saturating_from_integer(42);
|
|
let b = $name::saturating_from_integer(2);
|
|
assert_eq!($name::saturating_from_integer(21), a / b);
|
|
|
|
if $name::SIGNED {
|
|
let a = $name::saturating_from_integer(42);
|
|
let b = $name::saturating_from_integer(-2);
|
|
assert_eq!($name::saturating_from_integer(-21), a / b);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_from_integer_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
let accuracy = $name::accuracy();
|
|
|
|
// Cases where integer fits.
|
|
let a = $name::saturating_from_integer(42);
|
|
assert_eq!(a.into_inner(), 42 * accuracy);
|
|
|
|
let a = $name::saturating_from_integer(-42);
|
|
assert_eq!(a.into_inner(), 0.saturating_sub(42 * accuracy));
|
|
|
|
// Max/min integers that fit.
|
|
let a = $name::saturating_from_integer(inner_max / accuracy);
|
|
assert_eq!(a.into_inner(), (inner_max / accuracy) * accuracy);
|
|
|
|
let a = $name::saturating_from_integer(inner_min / accuracy);
|
|
assert_eq!(a.into_inner(), (inner_min / accuracy) * accuracy);
|
|
|
|
// Cases where integer doesn't fit, so it saturates.
|
|
let a = $name::saturating_from_integer(inner_max / accuracy + 1);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
let a = $name::saturating_from_integer((inner_min / accuracy).saturating_sub(1));
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
}
|
|
|
|
#[test]
|
|
fn checked_from_integer_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
let accuracy = $name::accuracy();
|
|
|
|
// Case where integer fits.
|
|
let a = $name::checked_from_integer::<$inner_type>(42)
|
|
.expect("42 * accuracy <= inner_max; qed");
|
|
assert_eq!(a.into_inner(), 42 * accuracy);
|
|
|
|
// Max integer that fit.
|
|
let a = $name::checked_from_integer::<$inner_type>(inner_max / accuracy)
|
|
.expect("(inner_max / accuracy) * accuracy <= inner_max; qed");
|
|
assert_eq!(a.into_inner(), (inner_max / accuracy) * accuracy);
|
|
|
|
// Case where integer doesn't fit, so it returns `None`.
|
|
let a = $name::checked_from_integer::<$inner_type>(inner_max / accuracy + 1);
|
|
assert_eq!(a, None);
|
|
|
|
if $name::SIGNED {
|
|
// Case where integer fits.
|
|
let a = $name::checked_from_integer::<$inner_type>(0.saturating_sub(42))
|
|
.expect("-42 * accuracy >= inner_min; qed");
|
|
assert_eq!(a.into_inner(), 0 - 42 * accuracy);
|
|
|
|
// Min integer that fit.
|
|
let a = $name::checked_from_integer::<$inner_type>(inner_min / accuracy)
|
|
.expect("(inner_min / accuracy) * accuracy <= inner_min; qed");
|
|
assert_eq!(a.into_inner(), (inner_min / accuracy) * accuracy);
|
|
|
|
// Case where integer doesn't fit, so it returns `None`.
|
|
let a = $name::checked_from_integer::<$inner_type>(inner_min / accuracy - 1);
|
|
assert_eq!(a, None);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn from_inner_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
|
|
assert_eq!(max(), $name::from_inner(inner_max));
|
|
assert_eq!(min(), $name::from_inner(inner_min));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "attempt to divide by zero")]
|
|
fn saturating_from_rational_panics_on_zero_divisor() {
|
|
let _ = $name::saturating_from_rational(1, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_from_rational_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
let accuracy = $name::accuracy();
|
|
|
|
let a = $name::saturating_from_rational(5, 2);
|
|
|
|
// Positive case: 2.5
|
|
assert_eq!(a.into_inner(), 25 * accuracy / 10);
|
|
|
|
// Max - 1.
|
|
let a = $name::saturating_from_rational(inner_max - 1, accuracy);
|
|
assert_eq!(a.into_inner(), inner_max - 1);
|
|
|
|
// Min + 1.
|
|
let a = $name::saturating_from_rational(inner_min + 1, accuracy);
|
|
assert_eq!(a.into_inner(), inner_min + 1);
|
|
|
|
// Max.
|
|
let a = $name::saturating_from_rational(inner_max, accuracy);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
// Min.
|
|
let a = $name::saturating_from_rational(inner_min, accuracy);
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
|
|
// Zero.
|
|
let a = $name::saturating_from_rational(0, 1);
|
|
assert_eq!(a.into_inner(), 0);
|
|
|
|
if $name::SIGNED {
|
|
// Negative case: -2.5
|
|
let a = $name::saturating_from_rational(-5, 2);
|
|
assert_eq!(a.into_inner(), 0 - 25 * accuracy / 10);
|
|
|
|
// Other negative case: -2.5
|
|
let a = $name::saturating_from_rational(5, -2);
|
|
assert_eq!(a.into_inner(), 0 - 25 * accuracy / 10);
|
|
|
|
// Other positive case: 2.5
|
|
let a = $name::saturating_from_rational(-5, -2);
|
|
assert_eq!(a.into_inner(), 25 * accuracy / 10);
|
|
|
|
// Max + 1, saturates.
|
|
let a = $name::saturating_from_rational(inner_max as u128 + 1, accuracy);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
// Min - 1, saturates.
|
|
let a = $name::saturating_from_rational(inner_max as u128 + 2, 0 - accuracy);
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, 0 - accuracy);
|
|
assert_eq!(a.into_inner(), 0 - inner_max);
|
|
|
|
let a = $name::saturating_from_rational(inner_min, 0 - accuracy);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
let a = $name::saturating_from_rational(inner_min + 1, 0 - accuracy);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
let a = $name::saturating_from_rational(inner_min, 0 - 1);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, 0 - 1);
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, 0 - inner_max);
|
|
assert_eq!(a.into_inner(), 0 - accuracy);
|
|
|
|
let a = $name::saturating_from_rational(0 - inner_max, inner_max);
|
|
assert_eq!(a.into_inner(), 0 - accuracy);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, 0 - 3 * accuracy);
|
|
assert_eq!(a.into_inner(), 0 - inner_max / 3);
|
|
|
|
let a = $name::saturating_from_rational(inner_min, 0 - accuracy / 3);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
let a = $name::saturating_from_rational(1, 0 - accuracy);
|
|
assert_eq!(a.into_inner(), 0.saturating_sub(1));
|
|
|
|
let a = $name::saturating_from_rational(inner_min, inner_min);
|
|
assert_eq!(a.into_inner(), accuracy);
|
|
|
|
// Out of accuracy.
|
|
let a = $name::saturating_from_rational(1, 0 - accuracy - 1);
|
|
assert_eq!(a.into_inner(), 0);
|
|
}
|
|
|
|
let a = $name::saturating_from_rational(inner_max - 1, accuracy);
|
|
assert_eq!(a.into_inner(), inner_max - 1);
|
|
|
|
let a = $name::saturating_from_rational(inner_min + 1, accuracy);
|
|
assert_eq!(a.into_inner(), inner_min + 1);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, 1);
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
let a = $name::saturating_from_rational(inner_min, 1);
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, inner_max);
|
|
assert_eq!(a.into_inner(), accuracy);
|
|
|
|
let a = $name::saturating_from_rational(inner_max, 3 * accuracy);
|
|
assert_eq!(a.into_inner(), inner_max / 3);
|
|
|
|
let a = $name::saturating_from_rational(inner_min, 2 * accuracy);
|
|
assert_eq!(a.into_inner(), inner_min / 2);
|
|
|
|
let a = $name::saturating_from_rational(inner_min, accuracy / 3);
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
|
|
let a = $name::saturating_from_rational(1, accuracy);
|
|
assert_eq!(a.into_inner(), 1);
|
|
|
|
// Out of accuracy.
|
|
let a = $name::saturating_from_rational(1, accuracy + 1);
|
|
assert_eq!(a.into_inner(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn checked_from_rational_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
let accuracy = $name::accuracy();
|
|
|
|
// Divide by zero => None.
|
|
let a = $name::checked_from_rational(1, 0);
|
|
assert_eq!(a, None);
|
|
|
|
// Max - 1.
|
|
let a = $name::checked_from_rational(inner_max - 1, accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), inner_max - 1);
|
|
|
|
// Min + 1.
|
|
let a = $name::checked_from_rational(inner_min + 1, accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), inner_min + 1);
|
|
|
|
// Max.
|
|
let a = $name::checked_from_rational(inner_max, accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), inner_max);
|
|
|
|
// Min.
|
|
let a = $name::checked_from_rational(inner_min, accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), inner_min);
|
|
|
|
// Max + 1 => Overflow => None.
|
|
let a = $name::checked_from_rational(inner_min, 0.saturating_sub(accuracy));
|
|
assert_eq!(a, None);
|
|
|
|
if $name::SIGNED {
|
|
// Min - 1 => Underflow => None.
|
|
let a = $name::checked_from_rational(
|
|
inner_max as u128 + 2,
|
|
0.saturating_sub(accuracy),
|
|
);
|
|
assert_eq!(a, None);
|
|
|
|
let a = $name::checked_from_rational(inner_max, 0 - 3 * accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), 0 - inner_max / 3);
|
|
|
|
let a = $name::checked_from_rational(inner_min, 0 - accuracy / 3);
|
|
assert_eq!(a, None);
|
|
|
|
let a = $name::checked_from_rational(1, 0 - accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), 0.saturating_sub(1));
|
|
|
|
let a = $name::checked_from_rational(1, 0 - accuracy - 1).unwrap();
|
|
assert_eq!(a.into_inner(), 0);
|
|
|
|
let a = $name::checked_from_rational(inner_min, accuracy / 3);
|
|
assert_eq!(a, None);
|
|
}
|
|
|
|
let a = $name::checked_from_rational(inner_max, 3 * accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), inner_max / 3);
|
|
|
|
let a = $name::checked_from_rational(inner_min, 2 * accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), inner_min / 2);
|
|
|
|
let a = $name::checked_from_rational(1, accuracy).unwrap();
|
|
assert_eq!(a.into_inner(), 1);
|
|
|
|
let a = $name::checked_from_rational(1, accuracy + 1).unwrap();
|
|
assert_eq!(a.into_inner(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn from_rational_works() {
|
|
let inner_max: u128 = <$name as FixedPointNumber>::Inner::max_value() as u128;
|
|
let inner_min: u128 = 0;
|
|
let accuracy: u128 = $name::accuracy() as u128;
|
|
|
|
// Max - 1.
|
|
let a = $name::from_rational(inner_max - 1, accuracy);
|
|
assert_eq!(a.into_inner() as u128, inner_max - 1);
|
|
|
|
// Min + 1.
|
|
let a = $name::from_rational(inner_min + 1, accuracy);
|
|
assert_eq!(a.into_inner() as u128, inner_min + 1);
|
|
|
|
// Max.
|
|
let a = $name::from_rational(inner_max, accuracy);
|
|
assert_eq!(a.into_inner() as u128, inner_max);
|
|
|
|
// Min.
|
|
let a = $name::from_rational(inner_min, accuracy);
|
|
assert_eq!(a.into_inner() as u128, inner_min);
|
|
|
|
let a = $name::from_rational(inner_max, 3 * accuracy);
|
|
assert_eq!(a.into_inner() as u128, inner_max / 3);
|
|
|
|
let a = $name::from_rational(1, accuracy);
|
|
assert_eq!(a.into_inner() as u128, 1);
|
|
|
|
let a = $name::from_rational(1, accuracy + 1);
|
|
assert_eq!(a.into_inner() as u128, 1);
|
|
|
|
let a = $name::from_rational_with_rounding(1, accuracy + 1, Rounding::Down);
|
|
assert_eq!(a.into_inner() as u128, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn checked_mul_int_works() {
|
|
let a = $name::saturating_from_integer(2);
|
|
// Max - 1.
|
|
assert_eq!(a.checked_mul_int((i128::MAX - 1) / 2), Some(i128::MAX - 1));
|
|
// Max.
|
|
assert_eq!(a.checked_mul_int(i128::MAX / 2), Some(i128::MAX - 1));
|
|
// Max + 1 => None.
|
|
assert_eq!(a.checked_mul_int(i128::MAX / 2 + 1), None);
|
|
|
|
if $name::SIGNED {
|
|
// Min - 1.
|
|
assert_eq!(a.checked_mul_int((i128::MIN + 1) / 2), Some(i128::MIN + 2));
|
|
// Min.
|
|
assert_eq!(a.checked_mul_int(i128::MIN / 2), Some(i128::MIN));
|
|
// Min + 1 => None.
|
|
assert_eq!(a.checked_mul_int(i128::MIN / 2 - 1), None);
|
|
|
|
let b = $name::saturating_from_rational(1, -2);
|
|
assert_eq!(b.checked_mul_int(42i128), Some(-21));
|
|
assert_eq!(b.checked_mul_int(u128::MAX), None);
|
|
assert_eq!(b.checked_mul_int(i128::MAX), Some(i128::MAX / -2));
|
|
assert_eq!(b.checked_mul_int(i128::MIN), Some(i128::MIN / -2));
|
|
}
|
|
|
|
let a = $name::saturating_from_rational(1, 2);
|
|
assert_eq!(a.checked_mul_int(42i128), Some(21));
|
|
assert_eq!(a.checked_mul_int(i128::MAX), Some(i128::MAX / 2));
|
|
assert_eq!(a.checked_mul_int(i128::MIN), Some(i128::MIN / 2));
|
|
|
|
let c = $name::saturating_from_integer(255);
|
|
assert_eq!(c.checked_mul_int(2i8), None);
|
|
assert_eq!(c.checked_mul_int(2i128), Some(510));
|
|
assert_eq!(c.checked_mul_int(i128::MAX), None);
|
|
assert_eq!(c.checked_mul_int(i128::MIN), None);
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_mul_int_works() {
|
|
let a = $name::saturating_from_integer(2);
|
|
// Max - 1.
|
|
assert_eq!(a.saturating_mul_int((i128::MAX - 1) / 2), i128::MAX - 1);
|
|
// Max.
|
|
assert_eq!(a.saturating_mul_int(i128::MAX / 2), i128::MAX - 1);
|
|
// Max + 1 => saturates to max.
|
|
assert_eq!(a.saturating_mul_int(i128::MAX / 2 + 1), i128::MAX);
|
|
|
|
// Min - 1.
|
|
assert_eq!(a.saturating_mul_int((i128::MIN + 1) / 2), i128::MIN + 2);
|
|
// Min.
|
|
assert_eq!(a.saturating_mul_int(i128::MIN / 2), i128::MIN);
|
|
// Min + 1 => saturates to min.
|
|
assert_eq!(a.saturating_mul_int(i128::MIN / 2 - 1), i128::MIN);
|
|
|
|
if $name::SIGNED {
|
|
let b = $name::saturating_from_rational(1, -2);
|
|
assert_eq!(b.saturating_mul_int(42i32), -21);
|
|
assert_eq!(b.saturating_mul_int(i128::MAX), i128::MAX / -2);
|
|
assert_eq!(b.saturating_mul_int(i128::MIN), i128::MIN / -2);
|
|
assert_eq!(b.saturating_mul_int(u128::MAX), u128::MIN);
|
|
}
|
|
|
|
let a = $name::saturating_from_rational(1, 2);
|
|
assert_eq!(a.saturating_mul_int(42i32), 21);
|
|
assert_eq!(a.saturating_mul_int(i128::MAX), i128::MAX / 2);
|
|
assert_eq!(a.saturating_mul_int(i128::MIN), i128::MIN / 2);
|
|
|
|
let c = $name::saturating_from_integer(255);
|
|
assert_eq!(c.saturating_mul_int(2i8), i8::MAX);
|
|
assert_eq!(c.saturating_mul_int(-2i8), i8::MIN);
|
|
assert_eq!(c.saturating_mul_int(i128::MAX), i128::MAX);
|
|
assert_eq!(c.saturating_mul_int(i128::MIN), i128::MIN);
|
|
}
|
|
|
|
#[test]
|
|
fn checked_mul_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
|
|
let a = $name::saturating_from_integer(2);
|
|
|
|
// Max - 1.
|
|
let b = $name::from_inner(inner_max - 1);
|
|
assert_eq!(a.checked_mul(&(b / 2.into())), Some(b));
|
|
|
|
// Max.
|
|
let c = $name::from_inner(inner_max);
|
|
assert_eq!(a.checked_mul(&(c / 2.into())), Some(b));
|
|
|
|
// Max + 1 => None.
|
|
let e = $name::from_inner(1);
|
|
assert_eq!(a.checked_mul(&(c / 2.into() + e)), None);
|
|
|
|
if $name::SIGNED {
|
|
// Min + 1.
|
|
let b = $name::from_inner(inner_min + 1) / 2.into();
|
|
let c = $name::from_inner(inner_min + 2);
|
|
assert_eq!(a.checked_mul(&b), Some(c));
|
|
|
|
// Min.
|
|
let b = $name::from_inner(inner_min) / 2.into();
|
|
let c = $name::from_inner(inner_min);
|
|
assert_eq!(a.checked_mul(&b), Some(c));
|
|
|
|
// Min - 1 => None.
|
|
let b = $name::from_inner(inner_min) / 2.into() - $name::from_inner(1);
|
|
assert_eq!(a.checked_mul(&b), None);
|
|
|
|
let c = $name::saturating_from_integer(255);
|
|
let b = $name::saturating_from_rational(1, -2);
|
|
|
|
assert_eq!(b.checked_mul(&42.into()), Some(0.saturating_sub(21).into()));
|
|
assert_eq!(
|
|
b.checked_mul(&$name::max_value()),
|
|
$name::max_value().checked_div(&0.saturating_sub(2).into())
|
|
);
|
|
assert_eq!(
|
|
b.checked_mul(&$name::min_value()),
|
|
$name::min_value().checked_div(&0.saturating_sub(2).into())
|
|
);
|
|
assert_eq!(c.checked_mul(&$name::min_value()), None);
|
|
}
|
|
|
|
let a = $name::saturating_from_rational(1, 2);
|
|
let c = $name::saturating_from_integer(255);
|
|
|
|
assert_eq!(a.checked_mul(&42.into()), Some(21.into()));
|
|
assert_eq!(c.checked_mul(&2.into()), Some(510.into()));
|
|
assert_eq!(c.checked_mul(&$name::max_value()), None);
|
|
assert_eq!(
|
|
a.checked_mul(&$name::max_value()),
|
|
$name::max_value().checked_div(&2.into())
|
|
);
|
|
assert_eq!(
|
|
a.checked_mul(&$name::min_value()),
|
|
$name::min_value().checked_div(&2.into())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn const_checked_mul_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
|
|
let a = $name::saturating_from_integer(2u32);
|
|
|
|
// Max - 1.
|
|
let b = $name::from_inner(inner_max - 1);
|
|
assert_eq!(a.const_checked_mul((b / 2.into())), Some(b));
|
|
|
|
// Max.
|
|
let c = $name::from_inner(inner_max);
|
|
assert_eq!(a.const_checked_mul((c / 2.into())), Some(b));
|
|
|
|
// Max + 1 => None.
|
|
let e = $name::from_inner(1);
|
|
assert_eq!(a.const_checked_mul((c / 2.into() + e)), None);
|
|
|
|
if $name::SIGNED {
|
|
// Min + 1.
|
|
let b = $name::from_inner(inner_min + 1) / 2.into();
|
|
let c = $name::from_inner(inner_min + 2);
|
|
assert_eq!(a.const_checked_mul(b), Some(c));
|
|
|
|
// Min.
|
|
let b = $name::from_inner(inner_min) / 2.into();
|
|
let c = $name::from_inner(inner_min);
|
|
assert_eq!(a.const_checked_mul(b), Some(c));
|
|
|
|
// Min - 1 => None.
|
|
let b = $name::from_inner(inner_min) / 2.into() - $name::from_inner(1);
|
|
assert_eq!(a.const_checked_mul(b), None);
|
|
|
|
let b = $name::saturating_from_rational(1i32, -2i32);
|
|
let c = $name::saturating_from_integer(-21i32);
|
|
let d = $name::saturating_from_integer(42);
|
|
|
|
assert_eq!(b.const_checked_mul(d), Some(c));
|
|
|
|
let minus_two = $name::saturating_from_integer(-2i32);
|
|
assert_eq!(
|
|
b.const_checked_mul($name::max_value()),
|
|
$name::max_value().const_checked_div(minus_two)
|
|
);
|
|
assert_eq!(
|
|
b.const_checked_mul($name::min_value()),
|
|
$name::min_value().const_checked_div(minus_two)
|
|
);
|
|
|
|
let c = $name::saturating_from_integer(255u32);
|
|
assert_eq!(c.const_checked_mul($name::min_value()), None);
|
|
}
|
|
|
|
let a = $name::saturating_from_rational(1i32, 2i32);
|
|
let c = $name::saturating_from_integer(255i32);
|
|
|
|
assert_eq!(a.const_checked_mul(42.into()), Some(21.into()));
|
|
assert_eq!(c.const_checked_mul(2.into()), Some(510.into()));
|
|
assert_eq!(c.const_checked_mul($name::max_value()), None);
|
|
assert_eq!(
|
|
a.const_checked_mul($name::max_value()),
|
|
$name::max_value().checked_div(&2.into())
|
|
);
|
|
assert_eq!(
|
|
a.const_checked_mul($name::min_value()),
|
|
$name::min_value().const_checked_div($name::saturating_from_integer(2))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn checked_div_int_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
let accuracy = $name::accuracy();
|
|
|
|
let a = $name::from_inner(inner_max);
|
|
let b = $name::from_inner(inner_min);
|
|
let c = $name::zero();
|
|
let d = $name::one();
|
|
let e = $name::saturating_from_integer(6);
|
|
let f = $name::saturating_from_integer(5);
|
|
|
|
assert_eq!(e.checked_div_int(2.into()), Some(3));
|
|
assert_eq!(f.checked_div_int(2.into()), Some(2));
|
|
|
|
assert_eq!(a.checked_div_int(i128::MAX), Some(0));
|
|
assert_eq!(a.checked_div_int(2), Some(inner_max / (2 * accuracy)));
|
|
assert_eq!(a.checked_div_int(inner_max / accuracy), Some(1));
|
|
assert_eq!(a.checked_div_int(1i8), None);
|
|
|
|
if b < c {
|
|
// Not executed by unsigned inners.
|
|
assert_eq!(
|
|
a.checked_div_int(0.saturating_sub(2)),
|
|
Some(0.saturating_sub(inner_max / (2 * accuracy)))
|
|
);
|
|
assert_eq!(
|
|
a.checked_div_int(0.saturating_sub(inner_max / accuracy)),
|
|
Some(0.saturating_sub(1))
|
|
);
|
|
assert_eq!(b.checked_div_int(i128::MIN), Some(0));
|
|
assert_eq!(b.checked_div_int(inner_min / accuracy), Some(1));
|
|
assert_eq!(b.checked_div_int(1i8), None);
|
|
assert_eq!(
|
|
b.checked_div_int(0.saturating_sub(2)),
|
|
Some(0.saturating_sub(inner_min / (2 * accuracy)))
|
|
);
|
|
assert_eq!(
|
|
b.checked_div_int(0.saturating_sub(inner_min / accuracy)),
|
|
Some(0.saturating_sub(1))
|
|
);
|
|
assert_eq!(c.checked_div_int(i128::MIN), Some(0));
|
|
assert_eq!(d.checked_div_int(i32::MIN), Some(0));
|
|
}
|
|
|
|
assert_eq!(b.checked_div_int(2), Some(inner_min / (2 * accuracy)));
|
|
|
|
assert_eq!(c.checked_div_int(1), Some(0));
|
|
assert_eq!(c.checked_div_int(i128::MAX), Some(0));
|
|
assert_eq!(c.checked_div_int(1i8), Some(0));
|
|
|
|
assert_eq!(d.checked_div_int(1), Some(1));
|
|
assert_eq!(d.checked_div_int(i32::MAX), Some(0));
|
|
assert_eq!(d.checked_div_int(1i8), Some(1));
|
|
|
|
assert_eq!(a.checked_div_int(0), None);
|
|
assert_eq!(b.checked_div_int(0), None);
|
|
assert_eq!(c.checked_div_int(0), None);
|
|
assert_eq!(d.checked_div_int(0), None);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "attempt to divide by zero")]
|
|
fn saturating_div_int_panics_when_divisor_is_zero() {
|
|
let _ = $name::one().saturating_div_int(0);
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_div_int_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
let accuracy = $name::accuracy();
|
|
|
|
let a = $name::saturating_from_integer(5);
|
|
assert_eq!(a.saturating_div_int(2), 2);
|
|
|
|
let a = $name::min_value();
|
|
assert_eq!(a.saturating_div_int(1i128), (inner_min / accuracy) as i128);
|
|
|
|
if $name::SIGNED {
|
|
let a = $name::saturating_from_integer(5);
|
|
assert_eq!(a.saturating_div_int(-2), -2);
|
|
|
|
let a = $name::min_value();
|
|
assert_eq!(a.saturating_div_int(-1i128), (inner_max / accuracy) as i128);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_abs_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
|
|
assert_eq!($name::from_inner(inner_max).saturating_abs(), $name::max_value());
|
|
assert_eq!($name::zero().saturating_abs(), 0.into());
|
|
|
|
if $name::SIGNED {
|
|
assert_eq!($name::from_inner(inner_min).saturating_abs(), $name::max_value());
|
|
assert_eq!(
|
|
$name::saturating_from_rational(-1, 2).saturating_abs(),
|
|
(1, 2).into()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_mul_acc_int_works() {
|
|
assert_eq!($name::zero().saturating_mul_acc_int(42i8), 42i8);
|
|
assert_eq!($name::one().saturating_mul_acc_int(42i8), 2 * 42i8);
|
|
|
|
assert_eq!($name::one().saturating_mul_acc_int(i128::MAX), i128::MAX);
|
|
assert_eq!($name::one().saturating_mul_acc_int(i128::MIN), i128::MIN);
|
|
|
|
assert_eq!($name::one().saturating_mul_acc_int(u128::MAX / 2), u128::MAX - 1);
|
|
assert_eq!($name::one().saturating_mul_acc_int(u128::MIN), u128::MIN);
|
|
|
|
if $name::SIGNED {
|
|
let a = $name::saturating_from_rational(-1, 2);
|
|
assert_eq!(a.saturating_mul_acc_int(42i8), 21i8);
|
|
assert_eq!(a.saturating_mul_acc_int(42u8), 21u8);
|
|
assert_eq!(a.saturating_mul_acc_int(u128::MAX - 1), u128::MAX / 2);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn saturating_pow_should_work() {
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(0),
|
|
$name::saturating_from_integer(1)
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(1),
|
|
$name::saturating_from_integer(2)
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(2),
|
|
$name::saturating_from_integer(4)
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(3),
|
|
$name::saturating_from_integer(8)
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(50),
|
|
$name::saturating_from_integer(1125899906842624i64)
|
|
);
|
|
|
|
assert_eq!($name::saturating_from_integer(1).saturating_pow(1000), (1).into());
|
|
assert_eq!(
|
|
$name::saturating_from_integer(1).saturating_pow(usize::MAX),
|
|
(1).into()
|
|
);
|
|
|
|
if $name::SIGNED {
|
|
// Saturating.
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(68),
|
|
$name::max_value()
|
|
);
|
|
|
|
assert_eq!($name::saturating_from_integer(-1).saturating_pow(1000), (1).into());
|
|
assert_eq!(
|
|
$name::saturating_from_integer(-1).saturating_pow(1001),
|
|
0.saturating_sub(1).into()
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(-1).saturating_pow(usize::MAX),
|
|
0.saturating_sub(1).into()
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(-1).saturating_pow(usize::MAX - 1),
|
|
(1).into()
|
|
);
|
|
}
|
|
|
|
assert_eq!(
|
|
$name::saturating_from_integer(114209).saturating_pow(5),
|
|
$name::max_value()
|
|
);
|
|
|
|
assert_eq!(
|
|
$name::saturating_from_integer(1).saturating_pow(usize::MAX),
|
|
(1).into()
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(0).saturating_pow(usize::MAX),
|
|
(0).into()
|
|
);
|
|
assert_eq!(
|
|
$name::saturating_from_integer(2).saturating_pow(usize::MAX),
|
|
$name::max_value()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn checked_div_works() {
|
|
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
|
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
|
|
|
|
let a = $name::from_inner(inner_max);
|
|
let b = $name::from_inner(inner_min);
|
|
let c = $name::zero();
|
|
let d = $name::one();
|
|
let e = $name::saturating_from_integer(6);
|
|
let f = $name::saturating_from_integer(5);
|
|
|
|
assert_eq!(e.checked_div(&2.into()), Some(3.into()));
|
|
assert_eq!(f.checked_div(&2.into()), Some((5, 2).into()));
|
|
|
|
assert_eq!(a.checked_div(&inner_max.into()), Some(1.into()));
|
|
assert_eq!(a.checked_div(&2.into()), Some($name::from_inner(inner_max / 2)));
|
|
assert_eq!(a.checked_div(&$name::max_value()), Some(1.into()));
|
|
assert_eq!(a.checked_div(&d), Some(a));
|
|
|
|
if b < c {
|
|
// Not executed by unsigned inners.
|
|
assert_eq!(
|
|
a.checked_div(&0.saturating_sub(2).into()),
|
|
Some($name::from_inner(0.saturating_sub(inner_max / 2)))
|
|
);
|
|
assert_eq!(
|
|
a.checked_div(&-$name::max_value()),
|
|
Some(0.saturating_sub(1).into())
|
|
);
|
|
assert_eq!(
|
|
b.checked_div(&0.saturating_sub(2).into()),
|
|
Some($name::from_inner(0.saturating_sub(inner_min / 2)))
|
|
);
|
|
assert_eq!(c.checked_div(&$name::max_value()), Some(0.into()));
|
|
assert_eq!(b.checked_div(&b), Some($name::one()));
|
|
}
|
|
|
|
assert_eq!(b.checked_div(&2.into()), Some($name::from_inner(inner_min / 2)));
|
|
assert_eq!(b.checked_div(&a), Some(0.saturating_sub(1).into()));
|
|
assert_eq!(c.checked_div(&1.into()), Some(0.into()));
|
|
assert_eq!(d.checked_div(&1.into()), Some(1.into()));
|
|
|
|
assert_eq!(a.checked_div(&$name::one()), Some(a));
|
|
assert_eq!(b.checked_div(&$name::one()), Some(b));
|
|
assert_eq!(c.checked_div(&$name::one()), Some(c));
|
|
assert_eq!(d.checked_div(&$name::one()), Some(d));
|
|
|
|
assert_eq!(a.checked_div(&$name::zero()), None);
|
|
assert_eq!(b.checked_div(&$name::zero()), None);
|
|
assert_eq!(c.checked_div(&$name::zero()), None);
|
|
assert_eq!(d.checked_div(&$name::zero()), None);
|
|
}
|
|
|
|
#[test]
|
|
fn is_positive_negative_works() {
|
|
let one = $name::one();
|
|
assert!(one.is_positive());
|
|
assert!(!one.is_negative());
|
|
|
|
let zero = $name::zero();
|
|
assert!(!zero.is_positive());
|
|
assert!(!zero.is_negative());
|
|
|
|
if $signed {
|
|
let minus_one = $name::saturating_from_integer(-1);
|
|
assert!(minus_one.is_negative());
|
|
assert!(!minus_one.is_positive());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn trunc_works() {
|
|
let n = $name::saturating_from_rational(5, 2).trunc();
|
|
assert_eq!(n, $name::saturating_from_integer(2));
|
|
|
|
if $name::SIGNED {
|
|
let n = $name::saturating_from_rational(-5, 2).trunc();
|
|
assert_eq!(n, $name::saturating_from_integer(-2));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn frac_works() {
|
|
let n = $name::saturating_from_rational(5, 2);
|
|
let i = n.trunc();
|
|
let f = n.frac();
|
|
|
|
assert_eq!(n, i + f);
|
|
|
|
let n = $name::saturating_from_rational(5, 2).frac().saturating_mul(10.into());
|
|
assert_eq!(n, 5.into());
|
|
|
|
let n = $name::saturating_from_rational(1, 2).frac().saturating_mul(10.into());
|
|
assert_eq!(n, 5.into());
|
|
|
|
if $name::SIGNED {
|
|
let n = $name::saturating_from_rational(-5, 2);
|
|
let i = n.trunc();
|
|
let f = n.frac();
|
|
assert_eq!(n, i - f);
|
|
|
|
// The sign is attached to the integer part unless it is zero.
|
|
let n = $name::saturating_from_rational(-5, 2).frac().saturating_mul(10.into());
|
|
assert_eq!(n, 5.into());
|
|
|
|
let n = $name::saturating_from_rational(-1, 2).frac().saturating_mul(10.into());
|
|
assert_eq!(n, 0.saturating_sub(5).into());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ceil_works() {
|
|
let n = $name::saturating_from_rational(5, 2);
|
|
assert_eq!(n.ceil(), 3.into());
|
|
|
|
let n = $name::saturating_from_rational(-5, 2);
|
|
assert_eq!(n.ceil(), 0.saturating_sub(2).into());
|
|
|
|
// On the limits:
|
|
let n = $name::max_value();
|
|
assert_eq!(n.ceil(), n.trunc());
|
|
|
|
let n = $name::min_value();
|
|
assert_eq!(n.ceil(), n.trunc());
|
|
}
|
|
|
|
#[test]
|
|
fn floor_works() {
|
|
let n = $name::saturating_from_rational(5, 2);
|
|
assert_eq!(n.floor(), 2.into());
|
|
|
|
let n = $name::saturating_from_rational(-5, 2);
|
|
assert_eq!(n.floor(), 0.saturating_sub(3).into());
|
|
|
|
// On the limits:
|
|
let n = $name::max_value();
|
|
assert_eq!(n.floor(), n.trunc());
|
|
|
|
let n = $name::min_value();
|
|
assert_eq!(n.floor(), n.trunc());
|
|
}
|
|
|
|
#[test]
|
|
fn round_works() {
|
|
let n = $name::zero();
|
|
assert_eq!(n.round(), n);
|
|
|
|
let n = $name::one();
|
|
assert_eq!(n.round(), n);
|
|
|
|
let n = $name::saturating_from_rational(5, 2);
|
|
assert_eq!(n.round(), 3.into());
|
|
|
|
let n = $name::saturating_from_rational(-5, 2);
|
|
assert_eq!(n.round(), 0.saturating_sub(3).into());
|
|
|
|
// Saturating:
|
|
let n = $name::max_value();
|
|
assert_eq!(n.round(), n.trunc());
|
|
|
|
let n = $name::min_value();
|
|
assert_eq!(n.round(), n.trunc());
|
|
|
|
// On the limit:
|
|
|
|
// floor(max - 1) + 0.33..
|
|
let n = $name::max_value()
|
|
.saturating_sub(1.into())
|
|
.trunc()
|
|
.saturating_add((1, 3).into());
|
|
|
|
assert_eq!(n.round(), ($name::max_value() - 1.into()).trunc());
|
|
|
|
// floor(max - 1) + 0.5
|
|
let n = $name::max_value()
|
|
.saturating_sub(1.into())
|
|
.trunc()
|
|
.saturating_add((1, 2).into());
|
|
|
|
assert_eq!(n.round(), $name::max_value().trunc());
|
|
|
|
if $name::SIGNED {
|
|
// floor(min + 1) - 0.33..
|
|
let n = $name::min_value()
|
|
.saturating_add(1.into())
|
|
.trunc()
|
|
.saturating_sub((1, 3).into());
|
|
|
|
assert_eq!(n.round(), ($name::min_value() + 1.into()).trunc());
|
|
|
|
// floor(min + 1) - 0.5
|
|
let n = $name::min_value()
|
|
.saturating_add(1.into())
|
|
.trunc()
|
|
.saturating_sub((1, 2).into());
|
|
|
|
assert_eq!(n.round(), $name::min_value().trunc());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn perthing_into_works() {
|
|
let ten_percent_percent: $name = Percent::from_percent(10).into();
|
|
assert_eq!(ten_percent_percent.into_inner(), $name::accuracy() / 10);
|
|
|
|
let ten_percent_permill: $name = Permill::from_percent(10).into();
|
|
assert_eq!(ten_percent_permill.into_inner(), $name::accuracy() / 10);
|
|
|
|
let ten_percent_perbill: $name = Perbill::from_percent(10).into();
|
|
assert_eq!(ten_percent_perbill.into_inner(), $name::accuracy() / 10);
|
|
|
|
let ten_percent_perquintill: $name = Perquintill::from_percent(10).into();
|
|
assert_eq!(ten_percent_perquintill.into_inner(), $name::accuracy() / 10);
|
|
}
|
|
|
|
#[test]
|
|
fn fmt_should_work() {
|
|
let zero = $name::zero();
|
|
assert_eq!(
|
|
format!("{:?}", zero),
|
|
format!("{}(0.{:0>weight$})", stringify!($name), 0, weight = precision())
|
|
);
|
|
|
|
let one = $name::one();
|
|
assert_eq!(
|
|
format!("{:?}", one),
|
|
format!("{}(1.{:0>weight$})", stringify!($name), 0, weight = precision())
|
|
);
|
|
|
|
let frac = $name::saturating_from_rational(1, 2);
|
|
assert_eq!(
|
|
format!("{:?}", frac),
|
|
format!("{}(0.{:0<weight$})", stringify!($name), 5, weight = precision())
|
|
);
|
|
|
|
let frac = $name::saturating_from_rational(5, 2);
|
|
assert_eq!(
|
|
format!("{:?}", frac),
|
|
format!("{}(2.{:0<weight$})", stringify!($name), 5, weight = precision())
|
|
);
|
|
|
|
let frac = $name::saturating_from_rational(314, 100);
|
|
assert_eq!(
|
|
format!("{:?}", frac),
|
|
format!("{}(3.{:0<weight$})", stringify!($name), 14, weight = precision())
|
|
);
|
|
|
|
if $name::SIGNED {
|
|
let neg = -$name::one();
|
|
assert_eq!(
|
|
format!("{:?}", neg),
|
|
format!("{}(-1.{:0>weight$})", stringify!($name), 0, weight = precision())
|
|
);
|
|
|
|
let frac = $name::saturating_from_rational(-314, 100);
|
|
assert_eq!(
|
|
format!("{:?}", frac),
|
|
format!("{}(-3.{:0<weight$})", stringify!($name), 14, weight = precision())
|
|
);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
implement_fixed!(
|
|
FixedI64,
|
|
test_fixed_i64,
|
|
i64,
|
|
true,
|
|
1_000_000_000,
|
|
"_Fixed Point 64 bits signed, range = [-9223372036.854775808, 9223372036.854775807]_",
|
|
);
|
|
|
|
implement_fixed!(
|
|
FixedU64,
|
|
test_fixed_u64,
|
|
u64,
|
|
false,
|
|
1_000_000_000,
|
|
"_Fixed Point 64 bits unsigned, range = [0.000000000, 18446744073.709551615]_",
|
|
);
|
|
|
|
implement_fixed!(
|
|
FixedI128,
|
|
test_fixed_i128,
|
|
i128,
|
|
true,
|
|
1_000_000_000_000_000_000,
|
|
"_Fixed Point 128 bits signed, range = \
|
|
[-170141183460469231731.687303715884105728, 170141183460469231731.687303715884105727]_",
|
|
);
|
|
|
|
implement_fixed!(
|
|
FixedU128,
|
|
test_fixed_u128,
|
|
u128,
|
|
false,
|
|
1_000_000_000_000_000_000,
|
|
"_Fixed Point 128 bits unsigned, range = \
|
|
[0.000000000000000000, 340282366920938463463.374607431768211455]_",
|
|
);
|