Enable fixed point u128 (#6214)

* Add fixed u128.

* remove move

* Change sat_from_integer impl.

* checked_pow is always positive

* Revert.

* rename fixed file

* Rename to FixedI

* rename fixed file

* Add newline.

* Use Multiplier in impls.

* Renames negate() to saturating_negate().

* Uncomment test.

* Add Signed to macro.

* Add some tests for Saturating trait.
This commit is contained in:
Marcio Diaz
2020-06-06 13:04:39 +02:00
committed by GitHub
parent 0761a8e0c3
commit 7c051caa42
11 changed files with 424 additions and 342 deletions
+2 -2
View File
@@ -26,7 +26,7 @@ use frame_support::{
};
use sp_core::{NeverNativeValue, traits::Externalities, storage::well_known_keys};
use sp_runtime::{
ApplyExtrinsicResult, Fixed128, FixedPointNumber,
ApplyExtrinsicResult, FixedI128, FixedPointNumber,
traits::Hash as HashT,
transaction_validity::InvalidTransaction,
};
@@ -53,7 +53,7 @@ use self::common::{*, sign};
pub const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY;
/// Default transfer fee
fn transfer_fee<E: Encode>(extrinsic: &E, fee_multiplier: Fixed128) -> Balance {
fn transfer_fee<E: Encode>(extrinsic: &E, fee_multiplier: FixedI128) -> Balance {
let length_fee = TransactionByteFee::get() * (extrinsic.encode().len() as Balance);
let base_weight = ExtrinsicBaseWeight::get();
+2 -2
View File
@@ -22,7 +22,7 @@ use frame_support::{
weights::{GetDispatchInfo, constants::ExtrinsicBaseWeight, IdentityFee, WeightToFeePolynomial},
};
use sp_core::NeverNativeValue;
use sp_runtime::{FixedPointNumber, Fixed128, Perbill};
use sp_runtime::{FixedPointNumber, FixedI128, Perbill};
use node_runtime::{
CheckedExtrinsic, Call, Runtime, Balances, TransactionPayment,
TransactionByteFee,
@@ -39,7 +39,7 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() {
let mut t = new_test_ext(COMPACT_CODE, false);
// initial fee multiplier must be zero
let mut prev_multiplier = Fixed128::from_inner(0);
let mut prev_multiplier = FixedI128::from_inner(0);
t.execute_with(|| {
assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier);
+35 -34
View File
@@ -19,8 +19,9 @@
use node_primitives::Balance;
use sp_runtime::traits::{Convert, Saturating};
use sp_runtime::{FixedPointNumber, Fixed128, Perquintill};
use sp_runtime::{FixedPointNumber, Perquintill};
use frame_support::traits::{OnUnbalanced, Currency, Get};
use pallet_transaction_payment::Multiplier;
use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance};
pub struct Author;
@@ -56,8 +57,8 @@ impl Convert<u128, Balance> for CurrencyToVoteHandler {
/// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees
pub struct TargetedFeeAdjustment<T>(sp_std::marker::PhantomData<T>);
impl<T: Get<Perquintill>> Convert<Fixed128, Fixed128> for TargetedFeeAdjustment<T> {
fn convert(multiplier: Fixed128) -> Fixed128 {
impl<T: Get<Perquintill>> Convert<Multiplier, Multiplier> for TargetedFeeAdjustment<T> {
fn convert(multiplier: Multiplier) -> Multiplier {
let max_weight = MaximumBlockWeight::get();
let block_weight = System::block_weight().total().min(max_weight);
let target_weight = (T::get() * max_weight) as u128;
@@ -67,13 +68,13 @@ impl<T: Get<Perquintill>> Convert<Fixed128, Fixed128> for TargetedFeeAdjustment<
let positive = block_weight >= target_weight;
let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight);
// safe, diff_abs cannot exceed u64.
let diff = Fixed128::saturating_from_rational(diff_abs, max_weight.max(1));
let diff = Multiplier::saturating_from_rational(diff_abs, max_weight.max(1));
let diff_squared = diff.saturating_mul(diff);
// 0.00004 = 4/100_000 = 40_000/10^9
let v = Fixed128::saturating_from_rational(4, 100_000);
let v = Multiplier::saturating_from_rational(4, 100_000);
// 0.00004^2 = 16/10^10 Taking the future /2 into account... 8/10^10
let v_squared_2 = Fixed128::saturating_from_rational(8, 10_000_000_000u64);
let v_squared_2 = Multiplier::saturating_from_rational(8, 10_000_000_000u64);
let first_term = v.saturating_mul(diff);
let second_term = v_squared_2.saturating_mul(diff_squared);
@@ -92,7 +93,7 @@ impl<T: Get<Perquintill>> Convert<Fixed128, Fixed128> for TargetedFeeAdjustment<
// multiplier. While at -1, it means that the network is so un-congested that all
// transactions have no weight fee. We stop here and only increase if the network
// became more busy.
.max(Fixed128::saturating_from_integer(-1))
.max(Multiplier::saturating_from_integer(-1))
}
}
}
@@ -114,7 +115,7 @@ mod tests {
}
// poc reference implementation.
fn fee_multiplier_update(block_weight: Weight, previous: Fixed128) -> Fixed128 {
fn fee_multiplier_update(block_weight: Weight, previous: Multiplier) -> Multiplier {
// maximum tx weight
let m = max() as f64;
// block weight always truncated to max weight
@@ -127,7 +128,7 @@ mod tests {
let s = block_weight;
let fm = v * (s/m - ss/m) + v.powi(2) * (s/m - ss/m).powi(2) / 2.0;
let addition_fm = Fixed128::from_inner((fm * Fixed128::accuracy() as f64).round() as i128);
let addition_fm = Multiplier::from_inner((fm * Multiplier::accuracy() as f64).round() as i128);
previous.saturating_add(addition_fm)
}
@@ -142,7 +143,7 @@ mod tests {
#[test]
fn fee_multiplier_update_poc_works() {
let fm = Fixed128::saturating_from_rational(0, 1);
let fm = Multiplier::saturating_from_rational(0, 1);
let test_set = vec![
(0, fm.clone()),
(100, fm.clone()),
@@ -156,7 +157,7 @@ mod tests {
fee_multiplier_update(w, fm),
TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm),
// Error is only 1 in 10^18
Fixed128::from_inner(1),
Multiplier::from_inner(1),
);
})
})
@@ -167,12 +168,12 @@ mod tests {
// just a few txs per_block.
let block_weight = 0;
run_with_system_weight(block_weight, || {
let mut fm = Fixed128::default();
let mut fm = Multiplier::default();
let mut iterations: u64 = 0;
loop {
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm);
fm = next;
if fm == Fixed128::saturating_from_integer(-1) { break; }
if fm == Multiplier::saturating_from_integer(-1) { break; }
iterations += 1;
}
println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm);
@@ -200,7 +201,7 @@ mod tests {
run_with_system_weight(block_weight, || {
// initial value configured on module
let mut fm = Fixed128::default();
let mut fm = Multiplier::default();
assert_eq!(fm, TransactionPayment::next_fee_multiplier());
let mut iterations: u64 = 0;
@@ -233,49 +234,49 @@ mod tests {
// and light blocks will have a weight multiplier less than 0.
run_with_system_weight(target() / 4, || {
// `fee_multiplier_update` is enough as it is the absolute truth value.
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::default());
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Multiplier::default());
assert_eq!(
next,
fee_multiplier_update(target() / 4 ,Fixed128::default())
fee_multiplier_update(target() / 4 ,Multiplier::default())
);
// Light block. Fee is reduced a little.
assert!(next < Fixed128::zero())
assert!(next < Multiplier::zero())
});
run_with_system_weight(target() / 2, || {
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::default());
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Multiplier::default());
assert_eq!(
next,
fee_multiplier_update(target() / 2 ,Fixed128::default())
fee_multiplier_update(target() / 2 ,Multiplier::default())
);
// Light block. Fee is reduced a little.
assert!(next < Fixed128::zero())
assert!(next < Multiplier::zero())
});
run_with_system_weight(target(), || {
// ideal. Original fee. No changes.
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::default());
assert_eq!(next, Fixed128::zero())
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Multiplier::default());
assert_eq!(next, Multiplier::zero())
});
run_with_system_weight(target() * 2, || {
// More than ideal. Fee is increased.
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::default());
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Multiplier::default());
assert_eq!(
next,
fee_multiplier_update(target() * 2 ,Fixed128::default())
fee_multiplier_update(target() * 2 ,Multiplier::default())
);
// Heavy block. Fee is increased a little.
assert!(next > Fixed128::zero())
assert!(next > Multiplier::zero())
});
}
#[test]
fn stateful_weight_mul_grow_to_infinity() {
run_with_system_weight(target() * 2, || {
let mut original = Fixed128::default();
let mut next = Fixed128::default();
let mut original = Multiplier::default();
let mut next = Multiplier::default();
(0..1_000).for_each(|_| {
next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(original);
@@ -293,7 +294,7 @@ mod tests {
#[test]
fn stateful_weight_mil_collapse_to_minus_one() {
run_with_system_weight(0, || {
let mut original = Fixed128::default(); // 0
let mut original = Multiplier::default(); // 0
let mut next;
// decreases
@@ -315,8 +316,8 @@ mod tests {
// ... stops going down at -1
assert_eq!(
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::saturating_from_integer(-1)),
Fixed128::saturating_from_integer(-1)
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Multiplier::saturating_from_integer(-1)),
Multiplier::saturating_from_integer(-1)
);
})
}
@@ -325,7 +326,7 @@ mod tests {
fn weight_to_fee_should_not_overflow_on_large_weights() {
let kb = 1024 as Weight;
let mb = kb * kb;
let max_fm = Fixed128::saturating_from_integer(i128::max_value());
let max_fm = Multiplier::saturating_from_integer(i128::max_value());
// check that for all values it can compute, correctly.
vec![
@@ -346,9 +347,9 @@ mod tests {
Weight::max_value(),
].into_iter().for_each(|i| {
run_with_system_weight(i, || {
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::default());
let truth = fee_multiplier_update(i, Fixed128::default());
assert_eq_error_rate!(truth, next, Fixed128::from_inner(50_000_000));
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(Multiplier::default());
let truth = fee_multiplier_update(i, Multiplier::default());
assert_eq_error_rate!(truth, next, Multiplier::from_inner(50_000_000));
});
});
+2 -2
View File
@@ -37,7 +37,7 @@ macro_rules! decl_tests {
($test:ty, $ext_builder:ty, $existential_deposit:expr) => {
use crate::*;
use sp_runtime::{FixedPointNumber, Fixed128, traits::{SignedExtension, BadOrigin}};
use sp_runtime::{FixedPointNumber, FixedI128, traits::{SignedExtension, BadOrigin}};
use frame_support::{
assert_noop, assert_ok, assert_err,
traits::{
@@ -162,7 +162,7 @@ macro_rules! decl_tests {
.monied(true)
.build()
.execute_with(|| {
pallet_transaction_payment::NextFeeMultiplier::put(Fixed128::saturating_from_integer(1));
pallet_transaction_payment::NextFeeMultiplier::put(FixedI128::saturating_from_integer(1));
Balances::set_lock(ID_1, &1, 10, WithdrawReason::Reserve.into());
assert_noop!(
<Balances as Currency<_>>::transfer(&1, &2, 1, AllowDeath),
+11 -9
View File
@@ -44,7 +44,7 @@ use frame_support::{
dispatch::DispatchResult,
};
use sp_runtime::{
Fixed128, FixedPointNumber, FixedPointOperand,
FixedI128, FixedPointNumber, FixedPointOperand,
transaction_validity::{
TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError,
TransactionValidity,
@@ -56,7 +56,9 @@ use sp_runtime::{
};
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
type Multiplier = Fixed128;
/// Fee multiplier.
pub type Multiplier = FixedI128;
type BalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
type NegativeImbalanceOf<T> =
@@ -617,7 +619,7 @@ mod tests {
.execute_with(||
{
let len = 10;
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2));
NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2));
let pre = ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
@@ -705,7 +707,7 @@ mod tests {
.execute_with(||
{
// all fees should be x1.5
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2));
NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2));
let len = 10;
assert!(
@@ -733,7 +735,7 @@ mod tests {
.execute_with(||
{
// all fees should be x1.5
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2));
NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2));
assert_eq!(
TransactionPayment::query_info(xt, len),
@@ -762,7 +764,7 @@ mod tests {
.execute_with(||
{
// Next fee multiplier is zero
assert_eq!(NextFeeMultiplier::get(), Fixed128::saturating_from_integer(0));
assert_eq!(NextFeeMultiplier::get(), Multiplier::saturating_from_integer(0));
// Tip only, no fees works
let dispatch_info = DispatchInfo {
@@ -802,7 +804,7 @@ mod tests {
.execute_with(||
{
// Add a next fee multiplier
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2)); // = 1/2 = .5
NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2)); // = 1/2 = .5
// Base fee is unaffected by multiplier
let dispatch_info = DispatchInfo {
weight: 0,
@@ -835,7 +837,7 @@ mod tests {
.execute_with(||
{
// Add a next fee multiplier
NextFeeMultiplier::put(Fixed128::saturating_from_rational(-1, 2)); // = -1/2 = -.5
NextFeeMultiplier::put(Multiplier::saturating_from_rational(-1, 2)); // = -1/2 = -.5
// Base fee is unaffected by multiplier
let dispatch_info = DispatchInfo {
weight: 0,
@@ -990,7 +992,7 @@ mod tests {
let len = 10;
let tip = 5;
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 4));
NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 4));
let pre = ChargeTransactionPayment::<Runtime>::from(tip)
.pre_dispatch(&2, CALL, &info, len)
@@ -33,5 +33,5 @@ name = "rational128"
path = "src/rational128.rs"
[[bin]]
name = "fixed"
path = "src/fixed.rs"
name = "fixed_point"
path = "src/fixed_point.rs"
@@ -16,19 +16,19 @@
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run fixed`. `honggfuzz` CLI options can
//! Running this fuzzer can be done with `cargo hfuzz run fixed_point`. `honggfuzz` CLI options can
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug fixed hfuzz_workspace/fixed/*.fuzz`.
//! `cargo hfuzz run-debug fixed_point hfuzz_workspace/fixed_point/*.fuzz`.
//!
//! # More information
//! More information about `honggfuzz` can be found
//! [here](https://docs.rs/honggfuzz/).
use honggfuzz::fuzz;
use sp_arithmetic::{FixedPointNumber, Fixed64, traits::Saturating};
use sp_arithmetic::{FixedPointNumber, FixedI64, traits::Saturating};
fn main() {
loop {
@@ -38,21 +38,21 @@ fn main() {
// Check `from_rational` and division are consistent.
if y != 0 {
let f1 = Fixed64::saturating_from_integer(x) / Fixed64::saturating_from_integer(y);
let f2 = Fixed64::saturating_from_rational(x, y);
let f1 = FixedI64::saturating_from_integer(x) / FixedI64::saturating_from_integer(y);
let f2 = FixedI64::saturating_from_rational(x, y);
assert_eq!(f1.into_inner(), f2.into_inner());
}
// Check `saturating_mul`.
let a = Fixed64::saturating_from_rational(2, 5);
let b = a.saturating_mul(Fixed64::saturating_from_integer(x));
let a = FixedI64::saturating_from_rational(2, 5);
let b = a.saturating_mul(FixedI64::saturating_from_integer(x));
let n = b.into_inner() as i128;
let m = 2i128 * x * Fixed64::accuracy() as i128 / 5i128;
let m = 2i128 * x * FixedI64::accuracy() as i128 / 5i128;
assert_eq!(n, m);
// Check `saturating_mul` and division are inverse.
if x != 0 {
assert_eq!(a, b / Fixed64::saturating_from_integer(x));
assert_eq!(a, b / FixedI64::saturating_from_integer(x));
}
// Check `reciprocal`.
@@ -60,22 +60,22 @@ fn main() {
assert_eq!(a, r);
// Check addition.
let a = Fixed64::saturating_from_integer(x);
let b = Fixed64::saturating_from_integer(y);
let c = Fixed64::saturating_from_integer(x.saturating_add(y));
let a = FixedI64::saturating_from_integer(x);
let b = FixedI64::saturating_from_integer(y);
let c = FixedI64::saturating_from_integer(x.saturating_add(y));
assert_eq!(a.saturating_add(b), c);
// Check substraction.
let a = Fixed64::saturating_from_integer(x);
let b = Fixed64::saturating_from_integer(y);
let c = Fixed64::saturating_from_integer(x.saturating_sub(y));
let a = FixedI64::saturating_from_integer(x);
let b = FixedI64::saturating_from_integer(y);
let c = FixedI64::saturating_from_integer(x.saturating_sub(y));
assert_eq!(a.saturating_sub(b), c);
// Check `saturating_mul_acc_int`.
let a = Fixed64::saturating_from_rational(2, 5);
let a = FixedI64::saturating_from_rational(2, 5);
let b = a.saturating_mul_acc_int(x);
let xx = Fixed64::saturating_from_integer(x);
let d = a.saturating_mul(xx).saturating_add(xx).into_inner() as i128 / Fixed64::accuracy() as i128;
let xx = FixedI64::saturating_from_integer(x);
let d = a.saturating_mul(xx).saturating_add(xx).into_inner() as i128 / FixedI64::accuracy() as i128;
assert_eq!(b, d);
});
}
@@ -22,7 +22,7 @@ use crate::{
helpers_128bit::multiply_by_rational, PerThing,
traits::{
SaturatedConversion, CheckedSub, CheckedAdd, CheckedMul, CheckedDiv, CheckedNeg,
Bounded, Saturating, UniqueSaturatedInto, Zero, One, Signed
Bounded, Saturating, UniqueSaturatedInto, Zero, One
},
};
@@ -59,11 +59,14 @@ pub trait FixedPointNumber:
+ Add + Sub + Div + Mul
{
/// The underlying data type used for this fixed point number.
type Inner: Debug + One + CheckedMul + CheckedDiv + CheckedNeg + Signed + FixedPointOperand;
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
@@ -75,16 +78,13 @@ pub trait FixedPointNumber:
/// Consumes `self` and returns the inner raw value.
fn into_inner(self) -> Self::Inner;
/// Returns the negation.
fn negate(self) -> Self {
Self::from_inner(-self.into_inner())
}
/// Creates self from an integer number `int`.
///
/// Returns `Self::max` or `Self::min` if `int` exceeds accuracy.
fn saturating_from_integer<N: UniqueSaturatedInto<Self::Inner>>(int: N) -> Self {
Self::from_inner(int.unique_saturated_into().saturating_mul(Self::DIV))
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(to_bound(int, 0)))
}
/// Creates `self` from an integer number `int`.
@@ -169,7 +169,7 @@ pub trait FixedPointNumber:
/// 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.negate().saturating_mul_int(n))
n.saturating_sub(Self::zero().saturating_sub(self).saturating_mul_int(n))
} else {
self.saturating_mul_int(n).saturating_add(n)
}
@@ -180,7 +180,7 @@ pub trait FixedPointNumber:
/// Returns `Self::max` if `self == Self::min`.
fn saturating_abs(self) -> Self {
let inner = self.into_inner();
if inner.is_positive() {
if inner >= Self::Inner::zero() {
self
} else {
Self::from_inner(inner.checked_neg().unwrap_or(Self::Inner::max_value()))
@@ -254,7 +254,11 @@ pub trait FixedPointNumber:
if self.is_negative() {
self.trunc()
} else {
self.saturating_add(Self::one()).trunc()
if self.frac() == Self::zero() {
self
} else {
self.saturating_add(Self::one()).trunc()
}
}
}
@@ -277,8 +281,11 @@ pub trait FixedPointNumber:
if n < Self::saturating_from_integer(5) {
self.trunc()
} else {
let extra = Self::saturating_from_integer(self.into_inner().signum());
(self.saturating_add(extra)).trunc()
if self.is_positive() {
self.saturating_add(Self::one()).trunc()
} else {
self.saturating_sub(Self::one()).trunc()
}
}
}
}
@@ -328,6 +335,7 @@ macro_rules! implement_fixed {
$name:ident,
$test_mod:ident,
$inner_type:ty,
$signed:tt,
$div:tt,
$title:expr $(,)?
) => {
@@ -353,6 +361,7 @@ macro_rules! implement_fixed {
type Inner = $inner_type;
const DIV: Self::Inner = $div;
const SIGNED: bool = $signed;
fn from_inner(inner: Self::Inner) -> Self {
Self(inner)
@@ -400,7 +409,7 @@ macro_rules! implement_fixed {
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
Self(<Self as FixedPointNumber>::Inner::zero() - self.0)
}
}
@@ -500,7 +509,7 @@ macro_rules! implement_fixed {
format!("{}{}", signum_for_zero, int)
};
let precision = (Self::accuracy() as f64).log10() as usize;
let fractional = format!("{:0>weight$}", (self.0 % Self::accuracy()).abs(), weight=precision);
let fractional = format!("{:0>weight$}", ((self.0 % Self::accuracy()) as i128).abs(), weight=precision);
write!(f, "{}({}.{})", stringify!($name), integral, fractional)
}
@@ -670,35 +679,38 @@ macro_rules! implement_fixed {
#[test]
fn op_neg_works() {
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);
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]
@@ -716,10 +728,11 @@ macro_rules! implement_fixed {
// Positive case: 6/2 = 3.
assert_eq!($name::saturating_from_integer(3), a + b);
let b = $name::saturating_from_rational(1, -2);
// Negative case: 4/2 = 2.
assert_eq!($name::saturating_from_integer(2), 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]
@@ -734,13 +747,8 @@ macro_rules! implement_fixed {
let a = $name::saturating_from_rational(5, 2);
let b = $name::saturating_from_rational(1, 2);
// Negative case: 4/2 = 2.
assert_eq!($name::saturating_from_integer(2), a - b);
let b = $name::saturating_from_rational(1, -2);
// Positive case: 6/2 = 3.
assert_eq!($name::saturating_from_integer(3), a - b);
assert_eq!($name::saturating_from_integer(-2), b.saturating_sub(a));
}
#[test]
@@ -771,9 +779,11 @@ macro_rules! implement_fixed {
#[test]
fn op_checked_div_overflow_works() {
let a = $name::min_value();
let b = (-1).into();
assert!(a.checked_div(&b).is_none());
if $name::SIGNED {
let a = $name::min_value();
let b = $name::zero().saturating_sub($name::one());
assert!(a.checked_div(&b).is_none());
}
}
#[test]
@@ -782,13 +792,15 @@ macro_rules! implement_fixed {
let b = $name::saturating_from_integer(2);
assert_eq!($name::saturating_from_integer(21), a / b);
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 from_integer_works() {
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();
@@ -798,7 +810,7 @@ macro_rules! implement_fixed {
assert_eq!(a.into_inner(), 42 * accuracy);
let a = $name::saturating_from_integer(-42);
assert_eq!(a.into_inner(), -42 * accuracy);
assert_eq!(a.into_inner(), 0.saturating_sub(42 * accuracy));
// Max/min integers that fit.
let a = $name::saturating_from_integer(inner_max / accuracy);
@@ -811,7 +823,7 @@ macro_rules! implement_fixed {
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 - 1);
let a = $name::saturating_from_integer((inner_min / accuracy).saturating_sub(1));
assert_eq!(a.into_inner(), inner_min);
}
@@ -821,30 +833,35 @@ macro_rules! implement_fixed {
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
let accuracy = $name::accuracy();
// Cases where integer fits.
// Case where integer fits.
let a = $name::checked_from_integer(42)
.expect("42 * accuracy <= inner_max; qed");
assert_eq!(a.into_inner(), 42 * accuracy);
let a = $name::checked_from_integer(-42)
.expect("-42 * accuracy >= inner_min; qed");
assert_eq!(a.into_inner(), -42 * accuracy);
// Max/min integers that fit.
// Max integer that fit.
let a = $name::checked_from_integer(inner_max / accuracy)
.expect("(inner_max / accuracy) * accuracy <= inner_max; qed");
assert_eq!(a.into_inner(), (inner_max / accuracy) * accuracy);
let a = $name::checked_from_integer(inner_min / accuracy)
.expect("(inner_min / accuracy) * accuracy <= inner_min; qed");
assert_eq!(a.into_inner(), (inner_min / accuracy) * accuracy);
// Cases where integer doesn't fit, so it returns `None`.
// Case where integer doesn't fit, so it returns `None`.
let a = $name::checked_from_integer(inner_max / accuracy + 1);
assert_eq!(a, None);
let a = $name::checked_from_integer(inner_min / accuracy - 1);
assert_eq!(a, None);
if $name::SIGNED {
// Case where integer fits.
let a = $name::checked_from_integer(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_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_min / accuracy - 1);
assert_eq!(a, None);
}
}
#[test]
@@ -873,21 +890,6 @@ macro_rules! implement_fixed {
// Positive case: 2.5
assert_eq!(a.into_inner(), 25 * accuracy / 10);
let a = $name::saturating_from_rational(-5, 2);
// Negative case: -2.5
assert_eq!(a.into_inner(), -25 * accuracy / 10);
let a = $name::saturating_from_rational(5, -2);
// Other negative case: -2.5
assert_eq!(a.into_inner(), -25 * accuracy / 10);
let a = $name::saturating_from_rational(-5, -2);
// Other 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);
@@ -904,26 +906,68 @@ macro_rules! implement_fixed {
let a = $name::saturating_from_rational(inner_min, accuracy);
assert_eq!(a.into_inner(), inner_min);
// 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, -accuracy);
assert_eq!(a.into_inner(), inner_min);
// Zero.
let a = $name::saturating_from_rational(0, 1);
assert_eq!(a.into_inner(), 0);
let a = $name::saturating_from_rational(inner_max, -accuracy);
assert_eq!(a.into_inner(), -inner_max);
if $name::SIGNED {
// Negative case: -2.5
let a = $name::saturating_from_rational(-5, 2);
assert_eq!(a.into_inner(), 0 - 25 * accuracy / 10);
let a = $name::saturating_from_rational(inner_min, -accuracy);
assert_eq!(a.into_inner(), inner_max);
// Other negative case: -2.5
let a = $name::saturating_from_rational(5, -2);
assert_eq!(a.into_inner(), 0 - 25 * accuracy / 10);
let a = $name::saturating_from_rational(inner_min + 1, -accuracy);
assert_eq!(a.into_inner(), inner_max);
// 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);
@@ -937,51 +981,24 @@ macro_rules! implement_fixed {
let a = $name::saturating_from_rational(inner_min, 1);
assert_eq!(a.into_inner(), inner_min);
let a = $name::saturating_from_rational(inner_min, -1);
assert_eq!(a.into_inner(), inner_max);
let a = $name::saturating_from_rational(inner_max, -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_min, inner_min);
assert_eq!(a.into_inner(), accuracy);
let a = $name::saturating_from_rational(inner_max, -inner_max);
assert_eq!(a.into_inner(), -accuracy);
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_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_max);
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);
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);
let a = $name::saturating_from_rational(1, -accuracy - 1);
assert_eq!(a.into_inner(), 0);
}
#[test]
@@ -1011,39 +1028,41 @@ macro_rules! implement_fixed {
assert_eq!(a.into_inner(), inner_min);
// Max + 1 => Overflow => None.
let a = $name::checked_from_rational(inner_min, -accuracy);
let a = $name::checked_from_rational(inner_min, 0.saturating_sub(accuracy));
assert_eq!(a, None);
// Min - 1 => Underflow => None.
let a = $name::checked_from_rational(inner_max as u128 + 2, -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_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(inner_min, accuracy / -3);
assert_eq!(a, None);
let a = $name::checked_from_rational(inner_min, accuracy / 3);
assert_eq!(a, None);
let a = $name::checked_from_rational(1, accuracy).unwrap();
assert_eq!(a.into_inner(), 1);
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);
let a = $name::checked_from_rational(1, -accuracy - 1).unwrap();
assert_eq!(a.into_inner(), 0);
}
#[test]
@@ -1056,24 +1075,26 @@ macro_rules! implement_fixed {
// Max + 1 => None.
assert_eq!(a.checked_mul_int(i128::max_value() / 2 + 1), None);
// Min - 1.
assert_eq!(a.checked_mul_int((i128::min_value() + 1) / 2), Some(i128::min_value() + 2));
// Min.
assert_eq!(a.checked_mul_int(i128::min_value() / 2), Some(i128::min_value()));
// Min + 1 => None.
assert_eq!(a.checked_mul_int(i128::min_value() / 2 - 1), None);
if $name::SIGNED {
// Min - 1.
assert_eq!(a.checked_mul_int((i128::min_value() + 1) / 2), Some(i128::min_value() + 2));
// Min.
assert_eq!(a.checked_mul_int(i128::min_value() / 2), Some(i128::min_value()));
// Min + 1 => None.
assert_eq!(a.checked_mul_int(i128::min_value() / 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_value()), None);
assert_eq!(b.checked_mul_int(i128::max_value()), Some(i128::max_value() / -2));
assert_eq!(b.checked_mul_int(i128::min_value()), Some(i128::min_value() / -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_value()), Some(i128::max_value() / 2));
assert_eq!(a.checked_mul_int(i128::min_value()), Some(i128::min_value() / 2));
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_value()), None);
assert_eq!(b.checked_mul_int(i128::max_value()), Some(i128::max_value() / -2));
assert_eq!(b.checked_mul_int(i128::min_value()), Some(i128::min_value() / -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));
@@ -1098,17 +1119,19 @@ macro_rules! implement_fixed {
// Min + 1 => saturates to min.
assert_eq!(a.saturating_mul_int(i128::min_value() / 2 - 1), i128::min_value());
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_value()), i128::max_value() / -2);
assert_eq!(b.saturating_mul_int(i128::min_value()), i128::min_value() / -2);
assert_eq!(b.saturating_mul_int(u128::max_value()), u128::min_value());
}
let a = $name::saturating_from_rational(1, 2);
assert_eq!(a.saturating_mul_int(42i32), 21);
assert_eq!(a.saturating_mul_int(i128::max_value()), i128::max_value() / 2);
assert_eq!(a.saturating_mul_int(i128::min_value()), i128::min_value() / 2);
let b = $name::saturating_from_rational(1, -2);
assert_eq!(b.saturating_mul_int(42i32), -21);
assert_eq!(b.saturating_mul_int(i128::max_value()), i128::max_value() / -2);
assert_eq!(b.saturating_mul_int(i128::min_value()), i128::min_value() / -2);
assert_eq!(b.saturating_mul_int(u128::max_value()), u128::min_value());
let c = $name::saturating_from_integer(255);
assert_eq!(c.saturating_mul_int(2i8), i8::max_value());
assert_eq!(c.saturating_mul_int(-2i8), i8::min_value());
@@ -1135,34 +1158,36 @@ macro_rules! implement_fixed {
let e = $name::from_inner(1);
assert_eq!(a.checked_mul(&(c/2.into()+e)), None);
// 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));
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.
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);
// 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 b = $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!(b.checked_mul(&42.into()), Some((-21).into()));
assert_eq!(c.checked_mul(&2.into()), Some(510.into()));
assert_eq!(b.checked_mul(&$name::max_value()), $name::max_value().checked_div(&(-2).into()));
assert_eq!(b.checked_mul(&$name::min_value()), $name::min_value().checked_div(&(-2).into()));
assert_eq!(c.checked_mul(&$name::max_value()), None);
assert_eq!(c.checked_mul(&$name::min_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()));
}
@@ -1188,25 +1213,27 @@ macro_rules! implement_fixed {
assert_eq!(a.checked_div_int(inner_max / accuracy), Some(1));
assert_eq!(a.checked_div_int(1i8), None);
assert_eq!(a.checked_div_int(-2), Some(-inner_max / (2 * accuracy)));
assert_eq!(a.checked_div_int(inner_max / -accuracy), Some(-1));
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_value()), 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_value()), Some(0));
assert_eq!(d.checked_div_int(i32::min_value()), Some(0));
}
assert_eq!(b.checked_div_int(i128::min_value()), Some(0));
assert_eq!(b.checked_div_int(2), Some(inner_min / (2 * accuracy)));
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(-2), Some(-(inner_min / (2 * accuracy))));
assert_eq!(b.checked_div_int(-(inner_min / accuracy)), Some(-1));
assert_eq!(c.checked_div_int(1), Some(0));
assert_eq!(c.checked_div_int(i128::max_value()), Some(0));
assert_eq!(c.checked_div_int(i128::min_value()), 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_value()), Some(0));
assert_eq!(d.checked_div_int(i32::min_value()), Some(0));
assert_eq!(d.checked_div_int(1i8), Some(1));
assert_eq!(a.checked_div_int(0), None);
@@ -1230,14 +1257,16 @@ macro_rules! implement_fixed {
let a = $name::saturating_from_integer(5);
assert_eq!(a.saturating_div_int(2), 2);
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);
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]
@@ -1245,10 +1274,13 @@ macro_rules! implement_fixed {
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
let inner_min = <$name as FixedPointNumber>::Inner::min_value();
assert_eq!($name::from_inner(inner_min).saturating_abs(), $name::max_value());
assert_eq!($name::from_inner(inner_max).saturating_abs(), $name::max_value());
assert_eq!($name::zero().saturating_abs(), 0.into());
assert_eq!($name::saturating_from_rational(-1, 2).saturating_abs(), (1, 2).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]
@@ -1262,10 +1294,12 @@ macro_rules! implement_fixed {
assert_eq!($name::one().saturating_mul_acc_int(u128::max_value() / 2), u128::max_value() - 1);
assert_eq!($name::one().saturating_mul_acc_int(u128::min_value()), u128::min_value());
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_value() - 1), u128::max_value() / 2);
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_value() - 1), u128::max_value() / 2);
}
}
#[test]
@@ -1277,15 +1311,18 @@ macro_rules! implement_fixed {
assert_eq!($name::saturating_from_integer(2).saturating_pow(50),
$name::saturating_from_integer(1125899906842624i64));
// 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(1000), (1).into());
assert_eq!($name::saturating_from_integer(-1).saturating_pow(1001), (-1).into());
assert_eq!($name::saturating_from_integer(1).saturating_pow(usize::max_value()), (1).into());
assert_eq!($name::saturating_from_integer(-1).saturating_pow(usize::max_value()), (-1).into());
assert_eq!($name::saturating_from_integer(-1).saturating_pow(usize::max_value() - 1), (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_value()), 0.saturating_sub(1).into());
assert_eq!($name::saturating_from_integer(-1).saturating_pow(usize::max_value() - 1), (1).into());
}
assert_eq!($name::saturating_from_integer(114209).saturating_pow(5), $name::max_value());
@@ -1314,19 +1351,18 @@ macro_rules! implement_fixed {
assert_eq!(a.checked_div(&$name::max_value()), Some(1.into()));
assert_eq!(a.checked_div(&d), Some(a));
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()));
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(&b), Some($name::one()));
assert_eq!(b.checked_div(&2.into()), Some($name::from_inner(inner_min / 2)));
assert_eq!(b.checked_div(&(-2).into()), Some($name::from_inner(inner_min / -2)));
assert_eq!(b.checked_div(&a), Some((-1).into()));
assert_eq!(b.checked_div(&a), Some(0.saturating_sub(1).into()));
assert_eq!(c.checked_div(&1.into()), Some(0.into()));
assert_eq!(c.checked_div(&$name::max_value()), Some(0.into()));
assert_eq!(c.checked_div(&$name::min_value()), Some(0.into()));
assert_eq!(d.checked_div(&1.into()), Some(1.into()));
assert_eq!(a.checked_div(&$name::one()), Some(a));
@@ -1345,8 +1381,10 @@ macro_rules! implement_fixed {
let n = $name::saturating_from_rational(5, 2).trunc();
assert_eq!(n, $name::saturating_from_integer(2));
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]
@@ -1357,12 +1395,6 @@ macro_rules! implement_fixed {
assert_eq!(n, i + f);
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());
@@ -1373,16 +1405,23 @@ macro_rules! implement_fixed {
.saturating_mul(10.into());
assert_eq!(n, 5.into());
// 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());
if $name::SIGNED {
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(-1, 2)
.frac()
.saturating_mul(10.into());
assert_eq!(n, (-5).into());
// 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]
@@ -1391,7 +1430,7 @@ macro_rules! implement_fixed {
assert_eq!(n.ceil(), 3.into());
let n = $name::saturating_from_rational(-5, 2);
assert_eq!(n.ceil(), (-2).into());
assert_eq!(n.ceil(), 0.saturating_sub(2).into());
// On the limits:
let n = $name::max_value();
@@ -1407,7 +1446,7 @@ macro_rules! implement_fixed {
assert_eq!(n.floor(), 2.into());
let n = $name::saturating_from_rational(-5, 2);
assert_eq!(n.floor(), (-3).into());
assert_eq!(n.floor(), 0.saturating_sub(3).into());
// On the limits:
let n = $name::max_value();
@@ -1429,7 +1468,7 @@ macro_rules! implement_fixed {
assert_eq!(n.round(), 3.into());
let n = $name::saturating_from_rational(-5, 2);
assert_eq!(n.round(), (-3).into());
assert_eq!(n.round(), 0.saturating_sub(3).into());
// Saturating:
let n = $name::max_value();
@@ -1448,14 +1487,6 @@ macro_rules! implement_fixed {
assert_eq!(n.round(), ($name::max_value() - 1.into()).trunc());
// 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(max - 1) + 0.5
let n = $name::max_value()
.saturating_sub(1.into())
@@ -1464,13 +1495,23 @@ macro_rules! implement_fixed {
assert_eq!(n.round(), $name::max_value().trunc());
// floor(min + 1) - 0.5
let n = $name::min_value()
.saturating_add(1.into())
.trunc()
.saturating_sub((1, 2).into());
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().trunc());
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]
@@ -1496,9 +1537,6 @@ macro_rules! implement_fixed {
let one = $name::one();
assert_eq!(format!("{:?}", one), format!("{}(1.{:0>weight$})", stringify!($name), 0, weight=precision()));
let neg = -$name::one();
assert_eq!(format!("{:?}", neg), 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()));
@@ -1508,26 +1546,43 @@ macro_rules! implement_fixed {
let frac = $name::saturating_from_rational(314, 100);
assert_eq!(format!("{:?}", frac), format!("{}(3.{:0<weight$})", stringify!($name), 14, 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!(
Fixed64,
test_fixed64,
FixedI64,
test_fixed_i64,
i64,
true,
1_000_000_000,
"_Fixed Point 64 bits, range = [-9223372036.854775808, 9223372036.854775807]_",
"_Fixed Point 64 bits signed, range = [-9223372036.854775808, 9223372036.854775807]_",
);
implement_fixed!(
Fixed128,
test_fixed128,
FixedI128,
test_fixed_i128,
i128,
true,
1_000_000_000_000_000_000,
"_Fixed Point 128 bits, range = \
"_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]_",
);
+18 -2
View File
@@ -37,10 +37,10 @@ pub mod biguint;
pub mod helpers_128bit;
pub mod traits;
mod per_things;
mod fixed;
mod fixed_point;
mod rational128;
pub use fixed::{FixedPointNumber, Fixed64, Fixed128, FixedPointOperand};
pub use fixed_point::{FixedPointNumber, FixedPointOperand, FixedI64, FixedI128, FixedU128};
pub use per_things::{PerThing, InnerOf, Percent, PerU16, Permill, Perbill, Perquintill};
pub use rational128::Rational128;
@@ -88,6 +88,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::Saturating;
use sp_std::cmp::Ordering;
#[test]
@@ -131,4 +132,19 @@ mod tests {
// maximum capacity of their type, e.g. PerU16.
let _ = PerU16::from_rational_approximation(17424870u32, 17424870);
}
#[test]
fn saturating_mul_works() {
assert_eq!(Saturating::saturating_mul(2, i32::min_value()), i32::min_value());
assert_eq!(Saturating::saturating_mul(2, i32::max_value()), i32::max_value());
}
#[test]
fn saturating_pow_works() {
assert_eq!(Saturating::saturating_pow(i32::min_value(), 0), 1);
assert_eq!(Saturating::saturating_pow(i32::max_value(), 0), 1);
assert_eq!(Saturating::saturating_pow(i32::min_value(), 3), i32::min_value());
assert_eq!(Saturating::saturating_pow(i32::min_value(), 2), i32::max_value());
assert_eq!(Saturating::saturating_pow(i32::max_value(), 2), i32::max_value());
}
}
@@ -145,7 +145,15 @@ impl<T: Clone + Zero + One + PartialOrd + CheckedMul + Bounded + num_traits::Sat
}
fn saturating_pow(self, exp: usize) -> Self {
checked_pow(self, exp).unwrap_or_else(Bounded::max_value)
let neg = self < T::zero() && exp % 2 != 0;
checked_pow(self, exp)
.unwrap_or_else(||
if neg {
Bounded::min_value()
} else {
Bounded::max_value()
}
)
}
}
+2 -2
View File
@@ -71,8 +71,8 @@ pub use sp_core::RuntimeDebug;
/// Re-export top-level arithmetic stuff.
pub use sp_arithmetic::{
Perquintill, Perbill, Permill, Percent, PerU16, Rational128, Fixed64, Fixed128,
PerThing, traits::SaturatedConversion, FixedPointNumber, FixedPointOperand,
PerThing, traits::SaturatedConversion, Perquintill, Perbill, Permill, Percent, PerU16,
Rational128, FixedI64, FixedI128, FixedU128, FixedPointNumber, FixedPointOperand,
};
/// Re-export 128 bit helpers.
pub use sp_arithmetic::helpers_128bit;