mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
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:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
+18
-18
@@ -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);
|
||||
});
|
||||
}
|
||||
+323
-268
@@ -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]_",
|
||||
);
|
||||
@@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user