diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 7799f0913a829..2bb444b47c9be 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -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(extrinsic: &E, fee_multiplier: Fixed128) -> Balance { +fn transfer_fee(extrinsic: &E, fee_multiplier: FixedI128) -> Balance { let length_fee = TransactionByteFee::get() * (extrinsic.encode().len() as Balance); let base_weight = ExtrinsicBaseWeight::get(); diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index a4fc3930da28c..280408357ed2e 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -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); diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index 0047ae5c1b6c8..c8f42f3f26649 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -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 for CurrencyToVoteHandler { /// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees pub struct TargetedFeeAdjustment(sp_std::marker::PhantomData); -impl> Convert for TargetedFeeAdjustment { - fn convert(multiplier: Fixed128) -> Fixed128 { +impl> Convert for TargetedFeeAdjustment { + 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> Convert 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> Convert 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::::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::::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::::convert(Fixed128::default()); + let next = TargetedFeeAdjustment::::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::::convert(Fixed128::default()); + let next = TargetedFeeAdjustment::::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::::convert(Fixed128::default()); - assert_eq!(next, Fixed128::zero()) + let next = TargetedFeeAdjustment::::convert(Multiplier::default()); + assert_eq!(next, Multiplier::zero()) }); run_with_system_weight(target() * 2, || { // More than ideal. Fee is increased. - let next = TargetedFeeAdjustment::::convert(Fixed128::default()); + let next = TargetedFeeAdjustment::::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::::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::::convert(Fixed128::saturating_from_integer(-1)), - Fixed128::saturating_from_integer(-1) + TargetedFeeAdjustment::::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::::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::::convert(Multiplier::default()); + let truth = fee_multiplier_update(i, Multiplier::default()); + assert_eq_error_rate!(truth, next, Multiplier::from_inner(50_000_000)); }); }); diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 9be64fc74493b..c49a04ae56572 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -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!( >::transfer(&1, &2, 1, AllowDeath), diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 78c638b4844e2..8355a58c525d0 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -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 = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = @@ -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::::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::::from(tip) .pre_dispatch(&2, CALL, &info, len) diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index f870152f54840..9058aaea0b6f0 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -33,5 +33,5 @@ name = "rational128" path = "src/rational128.rs" [[bin]] -name = "fixed" -path = "src/fixed.rs" \ No newline at end of file +name = "fixed_point" +path = "src/fixed_point.rs" diff --git a/primitives/arithmetic/fuzzer/src/fixed.rs b/primitives/arithmetic/fuzzer/src/fixed_point.rs similarity index 62% rename from primitives/arithmetic/fuzzer/src/fixed.rs rename to primitives/arithmetic/fuzzer/src/fixed_point.rs index 115d7dbbdba05..9a88197ac32ad 100644 --- a/primitives/arithmetic/fuzzer/src/fixed.rs +++ b/primitives/arithmetic/fuzzer/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); }); } diff --git a/primitives/arithmetic/src/fixed.rs b/primitives/arithmetic/src/fixed_point.rs similarity index 74% rename from primitives/arithmetic/src/fixed.rs rename to primitives/arithmetic/src/fixed_point.rs index bc657c5ede397..55581ff54cece 100644 --- a/primitives/arithmetic/src/fixed.rs +++ b/primitives/arithmetic/src/fixed_point.rs @@ -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>(int: N) -> Self { - Self::from_inner(int.unique_saturated_into().saturating_mul(Self::DIV)) + fn saturating_from_integer(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(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(::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 a = $name::zero(); let b = -a; - // Positive. - assert_eq!($name::saturating_from_integer(-5), b); + // Zero. + assert_eq!(a, b); - let a = $name::saturating_from_integer(-5); - let b = -a; + if $name::SIGNED { + let a = $name::saturating_from_integer(5); + let b = -a; - // Negative - assert_eq!($name::saturating_from_integer(5), b); + // Positive. + assert_eq!($name::saturating_from_integer(-5), b); - let a = $name::max_value(); - let b = -a; + let a = $name::saturating_from_integer(-5); + let b = -a; - // Max. - assert_eq!($name::min_value() + $name::from_inner(1), b); + // Negative + assert_eq!($name::saturating_from_integer(5), b); - let a = $name::min_value() + $name::from_inner(1); - let b = -a; + let a = $name::max_value(); + let b = -a; - // Min. - assert_eq!($name::max_value(), b); + // Max. + assert_eq!($name::min_value() + $name::from_inner(1), b); - let a = $name::zero(); - let b = -a; + let a = $name::min_value() + $name::from_inner(1); + let b = -a; - // Zero. - assert_eq!(a, b); + // 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()); - - 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] @@ -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() + 1.into()).trunc()); - assert_eq!(n.round(), $name::min_value().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.{:0weight$})", stringify!($name), 0, weight=precision())); + + let frac = $name::saturating_from_rational(-314, 100); + assert_eq!(format!("{:?}", frac), format!("{}(-3.{:0 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() + } + ) } } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 52ae46c662428..fe156fe738773 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -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;