mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 14:37:57 +00:00
002d9260f9
**Update:** Pushed additional changes based on the review comments. **This pull request fixes various spelling mistakes in this repository.** Most of the changes are contained in the first **3** commits: - `Fix spelling mistakes in comments and docs` - `Fix spelling mistakes in test names` - `Fix spelling mistakes in error messages, panic messages, logs and tracing` Other source code spelling mistakes are separated into individual commits for easier reviewing: - `Fix the spelling of 'authority'` - `Fix the spelling of 'REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY'` - `Fix the spelling of 'prev_enqueud_messages'` - `Fix the spelling of 'endpoint'` - `Fix the spelling of 'children'` - `Fix the spelling of 'PenpalSiblingSovereignAccount'` - `Fix the spelling of 'PenpalSudoAccount'` - `Fix the spelling of 'insufficient'` - `Fix the spelling of 'PalletXcmExtrinsicsBenchmark'` - `Fix the spelling of 'subtracted'` - `Fix the spelling of 'CandidatePendingAvailability'` - `Fix the spelling of 'exclusive'` - `Fix the spelling of 'until'` - `Fix the spelling of 'discriminator'` - `Fix the spelling of 'nonexistent'` - `Fix the spelling of 'subsystem'` - `Fix the spelling of 'indices'` - `Fix the spelling of 'committed'` - `Fix the spelling of 'topology'` - `Fix the spelling of 'response'` - `Fix the spelling of 'beneficiary'` - `Fix the spelling of 'formatted'` - `Fix the spelling of 'UNKNOWN_PROOF_REQUEST'` - `Fix the spelling of 'succeeded'` - `Fix the spelling of 'reopened'` - `Fix the spelling of 'proposer'` - `Fix the spelling of 'InstantiationNonce'` - `Fix the spelling of 'depositor'` - `Fix the spelling of 'expiration'` - `Fix the spelling of 'phantom'` - `Fix the spelling of 'AggregatedKeyValue'` - `Fix the spelling of 'randomness'` - `Fix the spelling of 'defendant'` - `Fix the spelling of 'AquaticMammal'` - `Fix the spelling of 'transactions'` - `Fix the spelling of 'PassingTracingSubscriber'` - `Fix the spelling of 'TxSignaturePayload'` - `Fix the spelling of 'versioning'` - `Fix the spelling of 'descendant'` - `Fix the spelling of 'overridden'` - `Fix the spelling of 'network'` Let me know if this structure is adequate. **Note:** The usage of the words `Merkle`, `Merkelize`, `Merklization`, `Merkelization`, `Merkleization`, is somewhat inconsistent but I left it as it is. ~~**Note:** In some places the term `Receival` is used to refer to message reception, IMO `Reception` is the correct word here, but I left it as it is.~~ ~~**Note:** In some places the term `Overlayed` is used instead of the more acceptable version `Overlaid` but I also left it as it is.~~ ~~**Note:** In some places the term `Applyable` is used instead of the correct version `Applicable` but I also left it as it is.~~ **Note:** Some usage of British vs American english e.g. `judgement` vs `judgment`, `initialise` vs `initialize`, `optimise` vs `optimize` etc. are both present in different places, but I suppose that's understandable given the number of contributors. ~~**Note:** There is a spelling mistake in `.github/CODEOWNERS` but it triggers errors in CI when I make changes to it, so I left it as it is.~~
2243 lines
66 KiB
Rust
2243 lines
66 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.
|
|
//! Similar to types that implement [`PerThing`](crate::per_things), these are also
|
|
//! fixed-point types, however, they are able to represent larger fractions:
|
|
#![doc = docify::embed!("./src/lib.rs", fixed_u64)]
|
|
//!
|
|
//! ### Fixed Point Types in Practice
|
|
//!
|
|
//! If one needs to exceed the value of one (1), then
|
|
//! [`FixedU64`](FixedU64) (and its signed and `u128` counterparts) can be utilized.
|
|
//! Take for example this very rudimentary pricing mechanism, where we wish to calculate the demand
|
|
//! / supply to get a price for some on-chain compute:
|
|
#![doc = docify::embed!(
|
|
"./src/lib.rs",
|
|
fixed_u64_block_computation_example
|
|
)]
|
|
//!
|
|
//! For a much more comprehensive example, be sure to look at the source for broker (the "coretime")
|
|
//! pallet.
|
|
//!
|
|
//! #### Fixed Point Types in Practice
|
|
//!
|
|
//! Just as with [`PerThing`](PerThing), you can also perform regular mathematical
|
|
//! expressions:
|
|
#![doc = docify::embed!(
|
|
"./src/lib.rs",
|
|
fixed_u64_operation_example
|
|
)]
|
|
//!
|
|
|
|
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 core::{
|
|
fmt::Debug,
|
|
ops::{self, Add, Div, Mul, Sub},
|
|
};
|
|
|
|
#[cfg(feature = "serde")]
|
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
#[cfg(all(not(feature = "std"), feature = "serde"))]
|
|
use alloc::string::{String, ToString};
|
|
|
|
/// Integer types that can be used to interact with `FixedPointNumber` implementations.
|
|
pub trait FixedPointOperand:
|
|
Copy
|
|
+ Clone
|
|
+ Bounded
|
|
+ Zero
|
|
+ Saturating
|
|
+ PartialOrd<Self>
|
|
+ UniqueSaturatedInto<u128>
|
|
+ TryFrom<u128>
|
|
+ CheckedNeg
|
|
{
|
|
}
|
|
|
|
impl<T> FixedPointOperand for T where
|
|
T: Copy
|
|
+ Clone
|
|
+ Bounded
|
|
+ Zero
|
|
+ Saturating
|
|
+ PartialOrd<Self>
|
|
+ UniqueSaturatedInto<u128>
|
|
+ TryFrom<u128>
|
|
+ CheckedNeg
|
|
{
|
|
}
|
|
|
|
/// 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 preferred 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 ::core::fmt::Debug for $name {
|
|
#[cfg(feature = "std")]
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::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 ::core::fmt::Formatter) -> ::core::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 ::core::fmt::Display for $name {
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl ::core::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 ::core::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]_",
|
|
);
|