mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 04:41:02 +00:00
Implement FixedPoint trait. (#5877)
* Implement Fixed trait. * Fix tests * Fix tests * Fix tests 2 * Address review comment regarding from_i129. * Remove precision by using log10() as suggested in review. * Add small comments. * Use checked versions + panic for ops::*. * Remove repeated test. * Uncomment test. * Remove casts. * Add more comments. * Add tests. * Panic on saturating_div_int * More tests. * More docs. * Saturating renames. * Fix to_bound doc. * Move some impl to trait. * Add range * Add macro pre. * More round() tests. * Delete confusion. * More impl to trait * Add doc for fixedpoint op. * Remove trailing spaces. * Suggested docs changes. * More tests and comments for roundings. * Some quickcheck tests. * Add missing panic, more test/comments. * Nits. * Rename. * Remove primitives-types import. * Apply review suggestions * Fix long lines and add some fuzz. * fix long line * Update fuzzer * Bump impl * fix warnings Co-authored-by: Gavin Wood <gavin@parity.io> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Generated
+2
-2
@@ -2057,9 +2057,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "honggfuzz"
|
||||
version = "0.5.47"
|
||||
version = "0.5.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3de2c3273ef7735df1c5a72128ca85b1d20105b9aac643cdfd7a6e581311150"
|
||||
checksum = "832bac18a82ec7d6c21887daa8616b238fe90d5d5e762d0d4b9372cdaa9e097f"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"lazy_static",
|
||||
|
||||
@@ -26,7 +26,7 @@ use frame_support::{
|
||||
};
|
||||
use sp_core::{NeverNativeValue, traits::Externalities, storage::well_known_keys};
|
||||
use sp_runtime::{
|
||||
ApplyExtrinsicResult, Fixed128,
|
||||
ApplyExtrinsicResult, Fixed128, FixedPointNumber,
|
||||
traits::Hash as HashT,
|
||||
transaction_validity::InvalidTransaction,
|
||||
};
|
||||
@@ -61,7 +61,7 @@ fn transfer_fee<E: Encode>(extrinsic: &E, fee_multiplier: Fixed128) -> Balance {
|
||||
let weight = default_transfer_call().get_dispatch_info().weight;
|
||||
let weight_fee = <Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&weight);
|
||||
|
||||
base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee)
|
||||
base_fee + fee_multiplier.saturating_mul_acc_int(length_fee + weight_fee)
|
||||
}
|
||||
|
||||
fn xt() -> UncheckedExtrinsic {
|
||||
|
||||
@@ -22,7 +22,7 @@ use frame_support::{
|
||||
weights::{GetDispatchInfo, constants::ExtrinsicBaseWeight, IdentityFee, WeightToFeePolynomial},
|
||||
};
|
||||
use sp_core::NeverNativeValue;
|
||||
use sp_runtime::{Fixed128, Perbill};
|
||||
use sp_runtime::{FixedPointNumber, Fixed128, 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_parts(0);
|
||||
let mut prev_multiplier = Fixed128::from_inner(0);
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier);
|
||||
|
||||
@@ -17,13 +17,10 @@
|
||||
|
||||
//! Some configurable implementations as associated type for the substrate runtime.
|
||||
|
||||
use core::num::NonZeroI128;
|
||||
use node_primitives::Balance;
|
||||
use sp_runtime::traits::{Convert, Saturating};
|
||||
use sp_runtime::{Fixed128, Perquintill};
|
||||
use frame_support::{
|
||||
traits::{OnUnbalanced, Currency, Get},
|
||||
};
|
||||
use sp_runtime::{FixedPointNumber, Fixed128, Perquintill};
|
||||
use frame_support::traits::{OnUnbalanced, Currency, Get};
|
||||
use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance};
|
||||
|
||||
pub struct Author;
|
||||
@@ -69,18 +66,14 @@ impl<T: Get<Perquintill>> Convert<Fixed128, Fixed128> for TargetedFeeAdjustment<
|
||||
// determines if the first_term is positive
|
||||
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 and it can always be computed safely even with the lossy
|
||||
// `Fixed128::from_rational`.
|
||||
let diff = Fixed128::from_rational(
|
||||
diff_abs as i128,
|
||||
NonZeroI128::new(max_weight.max(1) as i128).unwrap(),
|
||||
);
|
||||
// safe, diff_abs cannot exceed u64.
|
||||
let diff = Fixed128::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::from_rational(4, NonZeroI128::new(100_000).unwrap());
|
||||
let v = Fixed128::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::from_rational(8, NonZeroI128::new(10_000_000_000).unwrap());
|
||||
let v_squared_2 = Fixed128::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);
|
||||
@@ -99,7 +92,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::from_natural(-1))
|
||||
.max(Fixed128::saturating_from_integer(-1))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +104,6 @@ mod tests {
|
||||
use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime};
|
||||
use crate::{constants::currency::*, TransactionPayment, TargetBlockFullness};
|
||||
use frame_support::weights::{Weight, WeightToFeePolynomial};
|
||||
use core::num::NonZeroI128;
|
||||
|
||||
fn max() -> Weight {
|
||||
MaximumBlockWeight::get()
|
||||
@@ -135,7 +127,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_parts((fm * Fixed128::accuracy() as f64).round() as i128);
|
||||
let addition_fm = Fixed128::from_inner((fm * Fixed128::accuracy() as f64).round() as i128);
|
||||
previous.saturating_add(addition_fm)
|
||||
}
|
||||
|
||||
@@ -150,7 +142,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn fee_multiplier_update_poc_works() {
|
||||
let fm = Fixed128::from_rational(0, NonZeroI128::new(1).unwrap());
|
||||
let fm = Fixed128::saturating_from_rational(0, 1);
|
||||
let test_set = vec![
|
||||
(0, fm.clone()),
|
||||
(100, fm.clone()),
|
||||
@@ -164,7 +156,7 @@ mod tests {
|
||||
fee_multiplier_update(w, fm),
|
||||
TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm),
|
||||
// Error is only 1 in 10^18
|
||||
Fixed128::from_parts(1),
|
||||
Fixed128::from_inner(1),
|
||||
);
|
||||
})
|
||||
})
|
||||
@@ -180,7 +172,7 @@ mod tests {
|
||||
loop {
|
||||
let next = TargetedFeeAdjustment::<TargetBlockFullness>::convert(fm);
|
||||
fm = next;
|
||||
if fm == Fixed128::from_natural(-1) { break; }
|
||||
if fm == Fixed128::saturating_from_integer(-1) { break; }
|
||||
iterations += 1;
|
||||
}
|
||||
println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm);
|
||||
@@ -220,7 +212,7 @@ mod tests {
|
||||
iterations += 1;
|
||||
let fee =
|
||||
<Runtime as pallet_transaction_payment::Trait>::WeightToFee::calc(&tx_weight);
|
||||
let adjusted_fee = fm.saturated_multiply_accumulate(fee);
|
||||
let adjusted_fee = fm.saturating_mul_acc_int(fee);
|
||||
println!(
|
||||
"iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \
|
||||
{} cents, {} dollars",
|
||||
@@ -323,8 +315,8 @@ mod tests {
|
||||
|
||||
// ... stops going down at -1
|
||||
assert_eq!(
|
||||
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::from_natural(-1)),
|
||||
Fixed128::from_natural(-1)
|
||||
TargetedFeeAdjustment::<TargetBlockFullness>::convert(Fixed128::saturating_from_integer(-1)),
|
||||
Fixed128::saturating_from_integer(-1)
|
||||
);
|
||||
})
|
||||
}
|
||||
@@ -333,7 +325,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::from_natural(i128::max_value());
|
||||
let max_fm = Fixed128::saturating_from_integer(i128::max_value());
|
||||
|
||||
// check that for all values it can compute, correctly.
|
||||
vec![
|
||||
@@ -356,7 +348,7 @@ mod tests {
|
||||
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_parts(50_000_000));
|
||||
assert_eq_error_rate!(truth, next, Fixed128::from_inner(50_000_000));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
// implementation changes and behavior does not, then leave spec_version as
|
||||
// is and increment impl_version.
|
||||
spec_version: 250,
|
||||
impl_version: 0,
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ macro_rules! decl_tests {
|
||||
($test:ty, $ext_builder:ty, $existential_deposit:expr) => {
|
||||
|
||||
use crate::*;
|
||||
use sp_runtime::{Fixed128, traits::{SignedExtension, BadOrigin}};
|
||||
use sp_runtime::{FixedPointNumber, Fixed128, traits::{SignedExtension, BadOrigin}};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok, assert_err,
|
||||
traits::{
|
||||
@@ -154,7 +154,7 @@ macro_rules! decl_tests {
|
||||
.monied(true)
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
pallet_transaction_payment::NextFeeMultiplier::put(Fixed128::from_natural(1));
|
||||
pallet_transaction_payment::NextFeeMultiplier::put(Fixed128::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,
|
||||
Fixed128, FixedPointNumber,
|
||||
transaction_validity::{
|
||||
TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError,
|
||||
TransactionValidity,
|
||||
@@ -83,7 +83,7 @@ pub trait Trait: frame_system::Trait {
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as TransactionPayment {
|
||||
pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_parts(0);
|
||||
pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_inner(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ impl<T: Trait> Module<T> {
|
||||
// the adjustable part of the fee
|
||||
let adjustable_fee = len_fee.saturating_add(unadjusted_weight_fee);
|
||||
let targeted_fee_adjustment = NextFeeMultiplier::get();
|
||||
let adjusted_fee = targeted_fee_adjustment.saturated_multiply_accumulate(adjustable_fee.saturated_into());
|
||||
let adjusted_fee = targeted_fee_adjustment.saturating_mul_acc_int(adjustable_fee.saturated_into());
|
||||
|
||||
let base_fee = Self::weight_to_fee(T::ExtrinsicBaseWeight::get());
|
||||
base_fee.saturating_add(adjusted_fee.saturated_into()).saturating_add(tip)
|
||||
@@ -190,7 +190,7 @@ impl<T: Trait> Module<T> {
|
||||
{
|
||||
let fee = UniqueSaturatedInto::<u128>::unique_saturated_into(Self::weight_to_fee(weight));
|
||||
UniqueSaturatedFrom::unique_saturated_from(
|
||||
NextFeeMultiplier::get().saturated_multiply_accumulate(fee)
|
||||
NextFeeMultiplier::get().saturating_mul_acc_int(fee)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -329,7 +329,6 @@ impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> whe
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::num::NonZeroI128;
|
||||
use codec::Encode;
|
||||
use frame_support::{
|
||||
impl_outer_dispatch, impl_outer_origin, parameter_types,
|
||||
@@ -575,7 +574,7 @@ mod tests {
|
||||
.execute_with(||
|
||||
{
|
||||
let len = 10;
|
||||
NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap()));
|
||||
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2));
|
||||
|
||||
let pre = ChargeTransactionPayment::<Runtime>::from(5 /* tipped */)
|
||||
.pre_dispatch(&2, CALL, &info_from_weight(100), len)
|
||||
@@ -663,7 +662,7 @@ mod tests {
|
||||
.execute_with(||
|
||||
{
|
||||
// all fees should be x1.5
|
||||
NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap()));
|
||||
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2));
|
||||
let len = 10;
|
||||
|
||||
assert!(
|
||||
@@ -691,7 +690,7 @@ mod tests {
|
||||
.execute_with(||
|
||||
{
|
||||
// all fees should be x1.5
|
||||
NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap()));
|
||||
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2));
|
||||
|
||||
assert_eq!(
|
||||
TransactionPayment::query_info(xt, len),
|
||||
@@ -720,7 +719,7 @@ mod tests {
|
||||
.execute_with(||
|
||||
{
|
||||
// Next fee multiplier is zero
|
||||
assert_eq!(NextFeeMultiplier::get(), Fixed128::from_natural(0));
|
||||
assert_eq!(NextFeeMultiplier::get(), Fixed128::saturating_from_integer(0));
|
||||
|
||||
// Tip only, no fees works
|
||||
let dispatch_info = DispatchInfo {
|
||||
@@ -760,7 +759,7 @@ mod tests {
|
||||
.execute_with(||
|
||||
{
|
||||
// Add a next fee multiplier
|
||||
NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())); // = 1/2 = .5
|
||||
NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2)); // = 1/2 = .5
|
||||
// Base fee is unaffected by multiplier
|
||||
let dispatch_info = DispatchInfo {
|
||||
weight: 0,
|
||||
|
||||
@@ -20,12 +20,12 @@ num-traits = { version = "0.2.8", default-features = false }
|
||||
sp-std = { version = "2.0.0-dev", default-features = false, path = "../std" }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
sp-debug-derive = { version = "2.0.0-dev", default-features = false, path = "../../primitives/debug-derive" }
|
||||
primitive-types = { version = "0.7.0", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7.2"
|
||||
criterion = "0.3"
|
||||
serde_json = "1.0"
|
||||
primitive-types = "0.7.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -35,7 +35,6 @@ std = [
|
||||
"sp-std/std",
|
||||
"serde",
|
||||
"sp-debug-derive/std",
|
||||
"primitive-types/std",
|
||||
]
|
||||
|
||||
[[bench]]
|
||||
|
||||
-401
@@ -1,401 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6"
|
||||
|
||||
[[package]]
|
||||
name = "byte-slice-cast"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "c2-chacha"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "fixed-hash"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"rand",
|
||||
"rustc-hex",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "honggfuzz"
|
||||
version = "0.5.45"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"lazy_static",
|
||||
"memmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-codec"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "integer-sqrt"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitvec",
|
||||
"byte-slice-cast",
|
||||
"parity-scale-codec-derive",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec-derive"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
|
||||
[[package]]
|
||||
name = "primitive-types"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975"
|
||||
dependencies = [
|
||||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e"
|
||||
dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
||||
dependencies = [
|
||||
"c2-chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hex"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-arithmetic"
|
||||
version = "2.0.0-alpha.3"
|
||||
dependencies = [
|
||||
"integer-sqrt",
|
||||
"num-traits",
|
||||
"parity-scale-codec",
|
||||
"serde",
|
||||
"sp-debug-derive",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-arithmetic-fuzzer"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"honggfuzz",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"primitive-types",
|
||||
"sp-arithmetic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-debug-derive"
|
||||
version = "2.0.0-alpha.3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-std"
|
||||
version = "2.0.0-alpha.3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
"rustc-hex",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
@@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
sp-arithmetic = { version = "2.0.0-dev", path = ".." }
|
||||
honggfuzz = "0.5"
|
||||
honggfuzz = "0.5.49"
|
||||
primitive-types = "0.7.0"
|
||||
num-bigint = "0.2"
|
||||
num-traits = "0.2"
|
||||
@@ -31,3 +31,7 @@ path = "src/per_thing_rational.rs"
|
||||
[[bin]]
|
||||
name = "rational128"
|
||||
path = "src/rational128.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "fixed"
|
||||
path = "src/fixed.rs"
|
||||
@@ -0,0 +1,82 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Running
|
||||
//! Running this fuzzer can be done with `cargo hfuzz run fixed`. `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`.
|
||||
//!
|
||||
//! # More information
|
||||
//! More information about `honggfuzz` can be found
|
||||
//! [here](https://docs.rs/honggfuzz/).
|
||||
|
||||
use honggfuzz::fuzz;
|
||||
use sp_arithmetic::{FixedPointNumber, Fixed64, traits::Saturating};
|
||||
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (i32, i32)| {
|
||||
let x: i128 = data.0.into();
|
||||
let y: i128 = data.1.into();
|
||||
|
||||
// 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);
|
||||
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 n = b.into_inner() as i128;
|
||||
let m = 2i128 * x * Fixed64::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));
|
||||
}
|
||||
|
||||
// Check `reciprocal`.
|
||||
let r = a.reciprocal().unwrap().reciprocal().unwrap();
|
||||
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));
|
||||
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));
|
||||
assert_eq!(a.saturating_sub(b), c);
|
||||
|
||||
// Check `saturating_mul_acc_int`.
|
||||
let a = Fixed64::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;
|
||||
assert_eq!(b, d);
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,732 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use primitive_types::U256;
|
||||
use crate::{
|
||||
traits::{Bounded, Saturating, UniqueSaturatedInto, SaturatedConversion},
|
||||
PerThing, Perquintill,
|
||||
};
|
||||
use sp_std::{
|
||||
convert::{Into, TryFrom, TryInto},
|
||||
fmt, ops,
|
||||
num::NonZeroI128,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
/// A signed fixed-point number.
|
||||
/// Can hold any value in the range [-170_141_183_460_469_231_731, 170_141_183_460_469_231_731]
|
||||
/// with fixed-point accuracy of 10 ** 18.
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Fixed128(i128);
|
||||
|
||||
const DIV: i128 = 1_000_000_000_000_000_000;
|
||||
|
||||
impl Fixed128 {
|
||||
/// Create self from a natural number.
|
||||
///
|
||||
/// Note that this might be lossy.
|
||||
pub fn from_natural(int: i128) -> Self {
|
||||
Self(int.saturating_mul(DIV))
|
||||
}
|
||||
|
||||
/// Accuracy of `Fixed128`.
|
||||
pub const fn accuracy() -> i128 {
|
||||
DIV
|
||||
}
|
||||
|
||||
/// Raw constructor. Equal to `parts / DIV`.
|
||||
pub const fn from_parts(parts: i128) -> Self {
|
||||
Self(parts)
|
||||
}
|
||||
|
||||
/// Creates self from a rational number. Equal to `n/d`.
|
||||
///
|
||||
/// Note that this might be lossy. Only use this if you are sure that `n * DIV` can fit into an
|
||||
/// i128.
|
||||
pub fn from_rational<N: UniqueSaturatedInto<i128>>(n: N, d: NonZeroI128) -> Self {
|
||||
let n = n.unique_saturated_into();
|
||||
Self(n.saturating_mul(DIV.into()) / d.get())
|
||||
}
|
||||
|
||||
/// Consume self and return the inner raw `i128` value.
|
||||
///
|
||||
/// Note this is a low level function, as the returned value is represented with accuracy.
|
||||
pub fn deconstruct(self) -> i128 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Takes the reciprocal(inverse) of Fixed128, 1/x
|
||||
pub fn recip(&self) -> Option<Self> {
|
||||
Self::from_natural(1i128).checked_div(self)
|
||||
}
|
||||
|
||||
/// Checked add. Same semantic to `num_traits::CheckedAdd`.
|
||||
pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
|
||||
self.0.checked_add(rhs.0).map(Self)
|
||||
}
|
||||
|
||||
/// Checked sub. Same semantic to `num_traits::CheckedSub`.
|
||||
pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
|
||||
self.0.checked_sub(rhs.0).map(Self)
|
||||
}
|
||||
|
||||
/// Checked mul. Same semantic to `num_traits::CheckedMul`.
|
||||
pub fn checked_mul(&self, rhs: &Self) -> Option<Self> {
|
||||
let signum = self.0.signum() * rhs.0.signum();
|
||||
let mut lhs = self.0;
|
||||
if lhs.is_negative() {
|
||||
lhs = lhs.saturating_mul(-1);
|
||||
}
|
||||
let mut rhs: i128 = rhs.0.saturated_into();
|
||||
if rhs.is_negative() {
|
||||
rhs = rhs.saturating_mul(-1);
|
||||
}
|
||||
|
||||
U256::from(lhs)
|
||||
.checked_mul(U256::from(rhs))
|
||||
.and_then(|n| n.checked_div(U256::from(DIV)))
|
||||
.and_then(|n| TryInto::<i128>::try_into(n).ok())
|
||||
.map(|n| Self(n * signum))
|
||||
}
|
||||
|
||||
/// Checked div. Same semantic to `num_traits::CheckedDiv`.
|
||||
pub fn checked_div(&self, rhs: &Self) -> Option<Self> {
|
||||
if rhs.0.signum() == 0 {
|
||||
return None;
|
||||
}
|
||||
if self.0 == 0 {
|
||||
return Some(*self);
|
||||
}
|
||||
|
||||
let signum = self.0.signum() / rhs.0.signum();
|
||||
let mut lhs: i128 = self.0;
|
||||
if lhs.is_negative() {
|
||||
lhs = lhs.saturating_mul(-1);
|
||||
}
|
||||
let mut rhs: i128 = rhs.0.saturated_into();
|
||||
if rhs.is_negative() {
|
||||
rhs = rhs.saturating_mul(-1);
|
||||
}
|
||||
|
||||
U256::from(lhs)
|
||||
.checked_mul(U256::from(DIV))
|
||||
.and_then(|n| n.checked_div(U256::from(rhs)))
|
||||
.and_then(|n| TryInto::<i128>::try_into(n).ok())
|
||||
.map(|n| Self(n * signum))
|
||||
}
|
||||
|
||||
/// Checked mul for int type `N`.
|
||||
pub fn checked_mul_int<N>(&self, other: &N) -> Option<N>
|
||||
where
|
||||
N: Copy + TryFrom<i128> + TryInto<i128>,
|
||||
{
|
||||
N::try_into(*other).ok().and_then(|rhs| {
|
||||
let mut lhs = self.0;
|
||||
if lhs.is_negative() {
|
||||
lhs = lhs.saturating_mul(-1);
|
||||
}
|
||||
let mut rhs: i128 = rhs.saturated_into();
|
||||
let signum = self.0.signum() * rhs.signum();
|
||||
if rhs.is_negative() {
|
||||
rhs = rhs.saturating_mul(-1);
|
||||
}
|
||||
|
||||
U256::from(lhs)
|
||||
.checked_mul(U256::from(rhs))
|
||||
.and_then(|n| n.checked_div(U256::from(DIV)))
|
||||
.and_then(|n| TryInto::<i128>::try_into(n).ok())
|
||||
.and_then(|n| TryInto::<N>::try_into(n * signum).ok())
|
||||
})
|
||||
}
|
||||
|
||||
/// Checked mul for int type `N`.
|
||||
pub fn saturating_mul_int<N>(&self, other: &N) -> N
|
||||
where
|
||||
N: Copy + TryFrom<i128> + TryInto<i128> + Bounded,
|
||||
{
|
||||
self.checked_mul_int(other).unwrap_or_else(|| {
|
||||
N::try_into(*other)
|
||||
.map(|n| n.signum())
|
||||
.map(|n| n * self.0.signum())
|
||||
.map(|signum| {
|
||||
if signum.is_negative() {
|
||||
Bounded::min_value()
|
||||
} else {
|
||||
Bounded::max_value()
|
||||
}
|
||||
})
|
||||
.unwrap_or(Bounded::max_value())
|
||||
})
|
||||
}
|
||||
|
||||
/// Checked div for int type `N`.
|
||||
pub fn checked_div_int<N>(&self, other: &N) -> Option<N>
|
||||
where
|
||||
N: Copy + TryFrom<i128> + TryInto<i128>,
|
||||
{
|
||||
N::try_into(*other)
|
||||
.ok()
|
||||
.and_then(|n| self.0.checked_div(n))
|
||||
.and_then(|n| n.checked_div(DIV))
|
||||
.and_then(|n| TryInto::<N>::try_into(n).ok())
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Saturating absolute value. Returning MAX if `parts` == i128::MIN instead of overflowing.
|
||||
pub fn saturating_abs(&self) -> Self {
|
||||
if self.0 == i128::min_value() {
|
||||
return Fixed128::max_value();
|
||||
}
|
||||
|
||||
if self.0.is_negative() {
|
||||
Fixed128::from_parts(self.0 * -1)
|
||||
} else {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_positive(&self) -> bool {
|
||||
self.0.is_positive()
|
||||
}
|
||||
|
||||
pub fn is_negative(&self) -> bool {
|
||||
self.0.is_negative()
|
||||
}
|
||||
|
||||
/// Performs a saturated multiply and accumulate by unsigned number.
|
||||
///
|
||||
/// Returns a saturated `int + (self * int)`.
|
||||
pub fn saturated_multiply_accumulate<N>(self, int: N) -> N
|
||||
where
|
||||
N: TryFrom<u128> + From<u64> + UniqueSaturatedInto<u64> + Bounded + Clone + Saturating +
|
||||
ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
|
||||
ops::Add<N, Output=N>,
|
||||
{
|
||||
let div = DIV as u128;
|
||||
let positive = self.0 > 0;
|
||||
// safe to convert as absolute value.
|
||||
let parts = self.0.checked_abs().map(|v| v as u128).unwrap_or(i128::max_value() as u128 + 1);
|
||||
|
||||
|
||||
// will always fit.
|
||||
let natural_parts = parts / div;
|
||||
// might saturate.
|
||||
let natural_parts: N = natural_parts.saturated_into();
|
||||
// fractional parts can always fit into u64.
|
||||
let perquintill_parts = (parts % div) as u64;
|
||||
|
||||
let n = int.clone().saturating_mul(natural_parts);
|
||||
let p = Perquintill::from_parts(perquintill_parts) * int.clone();
|
||||
|
||||
// everything that needs to be either added or subtracted from the original weight.
|
||||
let excess = n.saturating_add(p);
|
||||
|
||||
if positive {
|
||||
int.saturating_add(excess)
|
||||
} else {
|
||||
int.saturating_sub(excess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait
|
||||
/// for safe addition.
|
||||
impl ops::Add for Fixed128 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait
|
||||
/// for safe subtraction.
|
||||
impl ops::Sub for Fixed128 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Saturating for Fixed128 {
|
||||
fn saturating_add(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_add(rhs.0))
|
||||
}
|
||||
|
||||
fn saturating_sub(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_sub(rhs.0))
|
||||
}
|
||||
|
||||
fn saturating_mul(self, rhs: Self) -> Self {
|
||||
self.checked_mul(&rhs).unwrap_or_else(|| {
|
||||
if (self.0.signum() * rhs.0.signum()).is_negative() {
|
||||
Bounded::min_value()
|
||||
} else {
|
||||
Bounded::max_value()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn saturating_pow(self, exp: usize) -> Self {
|
||||
if exp == 0 {
|
||||
return Self::from_natural(1);
|
||||
}
|
||||
|
||||
let exp = exp as u64;
|
||||
let msb_pos = 64 - exp.leading_zeros();
|
||||
|
||||
let mut result = Self::from_natural(1);
|
||||
let mut pow_val = self;
|
||||
for i in 0..msb_pos {
|
||||
if ((1 << i) & exp) > 0 {
|
||||
result = result.saturating_mul(pow_val);
|
||||
}
|
||||
pow_val = pow_val.saturating_mul(pow_val);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Bounded for Fixed128 {
|
||||
fn min_value() -> Self {
|
||||
Self(Bounded::min_value())
|
||||
}
|
||||
|
||||
fn max_value() -> Self {
|
||||
Self(Bounded::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fixed128 {
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let integral = {
|
||||
let int = self.0 / DIV;
|
||||
let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" };
|
||||
format!("{}{}", signum_for_zero, int)
|
||||
};
|
||||
let fractional = format!("{:0>18}", (self.0 % DIV).abs());
|
||||
write!(f, "Fixed128({}.{})", integral, fractional)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: PerThing> From<P> for Fixed128 {
|
||||
fn from(val: P) -> Self {
|
||||
let accuracy = P::ACCURACY.saturated_into().max(1) as i128;
|
||||
let value = val.deconstruct().saturated_into() as i128;
|
||||
Fixed128::from_rational(value, NonZeroI128::new(accuracy).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Fixed128 {
|
||||
fn i128_str(&self) -> String {
|
||||
format!("{}", &self.0)
|
||||
}
|
||||
|
||||
fn try_from_i128_str(s: &str) -> Result<Self, &'static str> {
|
||||
let parts: i128 = s.parse().map_err(|_| "invalid string input")?;
|
||||
Ok(Self::from_parts(parts))
|
||||
}
|
||||
}
|
||||
|
||||
// Manual impl `Serialize` as serde_json does not support i128.
|
||||
// TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed.
|
||||
#[cfg(feature = "std")]
|
||||
impl Serialize for Fixed128 {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.i128_str())
|
||||
}
|
||||
}
|
||||
|
||||
// Manual impl `Serialize` as serde_json does not support i128.
|
||||
// TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed.
|
||||
#[cfg(feature = "std")]
|
||||
impl<'de> Deserialize<'de> for Fixed128 {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Fixed128::try_from_i128_str(&s).map_err(|err_str| de::Error::custom(err_str))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Perbill, Percent, Permill, Perquintill};
|
||||
|
||||
fn max() -> Fixed128 {
|
||||
Fixed128::max_value()
|
||||
}
|
||||
|
||||
fn min() -> Fixed128 {
|
||||
Fixed128::min_value()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed128_semantics() {
|
||||
let a = Fixed128::from_rational(5, NonZeroI128::new(2).unwrap());
|
||||
let b = Fixed128::from_rational(10, NonZeroI128::new(4).unwrap());
|
||||
assert_eq!(a.0, 5 * DIV / 2);
|
||||
assert_eq!(a, b);
|
||||
|
||||
let a = Fixed128::from_rational(-5, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a, Fixed128::from_natural(-5));
|
||||
|
||||
let a = Fixed128::from_rational(5, NonZeroI128::new(-1).unwrap());
|
||||
assert_eq!(a, Fixed128::from_natural(-5));
|
||||
|
||||
// biggest value that can be created.
|
||||
assert_ne!(max(), Fixed128::from_natural(170_141_183_460_469_231_731));
|
||||
assert_eq!(max(), Fixed128::from_natural(170_141_183_460_469_231_732));
|
||||
|
||||
// the smallest value that can be created.
|
||||
assert_ne!(min(), Fixed128::from_natural(-170_141_183_460_469_231_731));
|
||||
assert_eq!(min(), Fixed128::from_natural(-170_141_183_460_469_231_732));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed128_operation() {
|
||||
let a = Fixed128::from_natural(2);
|
||||
let b = Fixed128::from_natural(1);
|
||||
assert_eq!(a.checked_add(&b), Some(Fixed128::from_natural(1 + 2)));
|
||||
assert_eq!(a.checked_sub(&b), Some(Fixed128::from_natural(2 - 1)));
|
||||
assert_eq!(a.checked_mul(&b), Some(Fixed128::from_natural(1 * 2)));
|
||||
assert_eq!(
|
||||
a.checked_div(&b),
|
||||
Some(Fixed128::from_rational(2, NonZeroI128::new(1).unwrap()))
|
||||
);
|
||||
|
||||
let a = Fixed128::from_rational(5, NonZeroI128::new(2).unwrap());
|
||||
let b = Fixed128::from_rational(3, NonZeroI128::new(2).unwrap());
|
||||
assert_eq!(
|
||||
a.checked_add(&b),
|
||||
Some(Fixed128::from_rational(8, NonZeroI128::new(2).unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
a.checked_sub(&b),
|
||||
Some(Fixed128::from_rational(2, NonZeroI128::new(2).unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
a.checked_mul(&b),
|
||||
Some(Fixed128::from_rational(15, NonZeroI128::new(4).unwrap()))
|
||||
);
|
||||
assert_eq!(
|
||||
a.checked_div(&b),
|
||||
Some(Fixed128::from_rational(10, NonZeroI128::new(6).unwrap()))
|
||||
);
|
||||
|
||||
let a = Fixed128::from_natural(120);
|
||||
assert_eq!(a.checked_div_int(&2i32), Some(60));
|
||||
|
||||
let a = Fixed128::from_rational(20, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.checked_div_int(&2i32), Some(10));
|
||||
|
||||
let a = Fixed128::from_natural(120);
|
||||
assert_eq!(a.checked_mul_int(&2i32), Some(240));
|
||||
|
||||
let a = Fixed128::from_rational(1, NonZeroI128::new(2).unwrap());
|
||||
assert_eq!(a.checked_mul_int(&20i32), Some(10));
|
||||
|
||||
let a = Fixed128::from_rational(-1, NonZeroI128::new(2).unwrap());
|
||||
assert_eq!(a.checked_mul_int(&20i32), Some(-10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_mul_should_work() {
|
||||
let a = Fixed128::from_natural(-1);
|
||||
assert_eq!(min().saturating_mul(a), max());
|
||||
|
||||
assert_eq!(Fixed128::from_natural(125).saturating_mul(a).deconstruct(), -125 * DIV);
|
||||
|
||||
let a = Fixed128::from_rational(1, NonZeroI128::new(5).unwrap());
|
||||
assert_eq!(Fixed128::from_natural(125).saturating_mul(a).deconstruct(), 25 * DIV);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_mul_int_works() {
|
||||
let a = Fixed128::from_rational(10, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.saturating_mul_int(&i32::max_value()), i32::max_value());
|
||||
|
||||
let a = Fixed128::from_rational(-10, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.saturating_mul_int(&i32::max_value()), i32::min_value());
|
||||
|
||||
let a = Fixed128::from_rational(3, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.saturating_mul_int(&100i8), i8::max_value());
|
||||
|
||||
let a = Fixed128::from_rational(10, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.saturating_mul_int(&123i128), 1230);
|
||||
|
||||
let a = Fixed128::from_rational(-10, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.saturating_mul_int(&123i128), -1230);
|
||||
|
||||
assert_eq!(max().saturating_mul_int(&2i128), 340_282_366_920_938_463_463);
|
||||
|
||||
assert_eq!(max().saturating_mul_int(&i128::min_value()), i128::min_value());
|
||||
|
||||
assert_eq!(min().saturating_mul_int(&i128::max_value()), i128::min_value());
|
||||
|
||||
assert_eq!(min().saturating_mul_int(&i128::min_value()), i128::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_works() {
|
||||
assert_eq!(Fixed128::zero(), Fixed128::from_natural(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_zero_works() {
|
||||
assert!(Fixed128::zero().is_zero());
|
||||
assert!(!Fixed128::from_natural(1).is_zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_with_zero_should_be_none() {
|
||||
let a = Fixed128::from_natural(1);
|
||||
let b = Fixed128::from_natural(0);
|
||||
assert_eq!(a.checked_div(&b), None);
|
||||
assert_eq!(b.checked_div(&a), Some(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_int_with_zero_should_be_none() {
|
||||
let a = Fixed128::from_natural(1);
|
||||
assert_eq!(a.checked_div_int(&0i32), None);
|
||||
let a = Fixed128::from_natural(0);
|
||||
assert_eq!(a.checked_div_int(&1i32), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_with_zero_dividend_should_be_zero() {
|
||||
let a = Fixed128::zero();
|
||||
let b = Fixed128::from_parts(1);
|
||||
|
||||
assert_eq!(a.checked_div(&b), Some(Fixed128::zero()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn under_flow_should_be_none() {
|
||||
let b = Fixed128::from_natural(1);
|
||||
assert_eq!(min().checked_sub(&b), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn over_flow_should_be_none() {
|
||||
let a = Fixed128::from_parts(i128::max_value() - 1);
|
||||
let b = Fixed128::from_parts(2);
|
||||
assert_eq!(a.checked_add(&b), None);
|
||||
|
||||
let a = Fixed128::max_value();
|
||||
let b = Fixed128::from_rational(2, NonZeroI128::new(1).unwrap());
|
||||
assert_eq!(a.checked_mul(&b), None);
|
||||
|
||||
let a = Fixed128::from_natural(255);
|
||||
let b = 2u8;
|
||||
assert_eq!(a.checked_mul_int(&b), None);
|
||||
|
||||
let a = Fixed128::from_natural(256);
|
||||
let b = 1u8;
|
||||
assert_eq!(a.checked_div_int(&b), None);
|
||||
|
||||
let a = Fixed128::from_natural(256);
|
||||
let b = -1i8;
|
||||
assert_eq!(a.checked_div_int(&b), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_int_should_work() {
|
||||
// 256 / 10 = 25 (25.6 as int = 25)
|
||||
let a = Fixed128::from_natural(256);
|
||||
let result = a.checked_div_int(&10i128).unwrap();
|
||||
assert_eq!(result, 25);
|
||||
|
||||
// 256 / 100 = 2 (2.56 as int = 2)
|
||||
let a = Fixed128::from_natural(256);
|
||||
let result = a.checked_div_int(&100i128).unwrap();
|
||||
assert_eq!(result, 2);
|
||||
|
||||
// 256 / 1000 = 0 (0.256 as int = 0)
|
||||
let a = Fixed128::from_natural(256);
|
||||
let result = a.checked_div_int(&1000i128).unwrap();
|
||||
assert_eq!(result, 0);
|
||||
|
||||
// 256 / -1 = -256
|
||||
let a = Fixed128::from_natural(256);
|
||||
let result = a.checked_div_int(&-1i128).unwrap();
|
||||
assert_eq!(result, -256);
|
||||
|
||||
// -256 / -1 = 256
|
||||
let a = Fixed128::from_natural(-256);
|
||||
let result = a.checked_div_int(&-1i128).unwrap();
|
||||
assert_eq!(result, 256);
|
||||
|
||||
// 10 / -5 = -2
|
||||
let a = Fixed128::from_rational(20, NonZeroI128::new(2).unwrap());
|
||||
let result = a.checked_div_int(&-5i128).unwrap();
|
||||
assert_eq!(result, -2);
|
||||
|
||||
// -170_141_183_460_469_231_731 / -2 = 85_070_591_730_234_615_865
|
||||
let result = min().checked_div_int(&-2i128).unwrap();
|
||||
assert_eq!(result, 85_070_591_730_234_615_865);
|
||||
|
||||
// 85_070_591_730_234_615_865 * -2 = -170_141_183_460_469_231_730
|
||||
let result = Fixed128::from_natural(result).checked_mul_int(&-2i128).unwrap();
|
||||
assert_eq!(result, -170_141_183_460_469_231_730);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn perthing_into_fixed_i128() {
|
||||
let ten_percent_percent: Fixed128 = Percent::from_percent(10).into();
|
||||
assert_eq!(ten_percent_percent.deconstruct(), DIV / 10);
|
||||
|
||||
let ten_percent_permill: Fixed128 = Permill::from_percent(10).into();
|
||||
assert_eq!(ten_percent_permill.deconstruct(), DIV / 10);
|
||||
|
||||
let ten_percent_perbill: Fixed128 = Perbill::from_percent(10).into();
|
||||
assert_eq!(ten_percent_perbill.deconstruct(), DIV / 10);
|
||||
|
||||
let ten_percent_perquintill: Fixed128 = Perquintill::from_percent(10).into();
|
||||
assert_eq!(ten_percent_perquintill.deconstruct(), DIV / 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recip_should_work() {
|
||||
let a = Fixed128::from_natural(2);
|
||||
assert_eq!(
|
||||
a.recip(),
|
||||
Some(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap()))
|
||||
);
|
||||
|
||||
let a = Fixed128::from_natural(2);
|
||||
assert_eq!(a.recip().unwrap().checked_mul_int(&4i32), Some(2i32));
|
||||
|
||||
let a = Fixed128::from_rational(100, NonZeroI128::new(121).unwrap());
|
||||
assert_eq!(
|
||||
a.recip(),
|
||||
Some(Fixed128::from_rational(121, NonZeroI128::new(100).unwrap()))
|
||||
);
|
||||
|
||||
let a = Fixed128::from_rational(1, NonZeroI128::new(2).unwrap());
|
||||
assert_eq!(a.recip().unwrap().checked_mul(&a), Some(Fixed128::from_natural(1)));
|
||||
|
||||
let a = Fixed128::from_natural(0);
|
||||
assert_eq!(a.recip(), None);
|
||||
|
||||
let a = Fixed128::from_rational(-1, NonZeroI128::new(2).unwrap());
|
||||
assert_eq!(a.recip(), Some(Fixed128::from_natural(-2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_should_work() {
|
||||
let two_point_five = Fixed128::from_rational(5, NonZeroI128::new(2).unwrap());
|
||||
let serialized = serde_json::to_string(&two_point_five).unwrap();
|
||||
assert_eq!(serialized, "\"2500000000000000000\"");
|
||||
let deserialized: Fixed128 = serde_json::from_str(&serialized).unwrap();
|
||||
assert_eq!(deserialized, two_point_five);
|
||||
|
||||
let minus_two_point_five = Fixed128::from_rational(-5, NonZeroI128::new(2).unwrap());
|
||||
let serialized = serde_json::to_string(&minus_two_point_five).unwrap();
|
||||
assert_eq!(serialized, "\"-2500000000000000000\"");
|
||||
let deserialized: Fixed128 = serde_json::from_str(&serialized).unwrap();
|
||||
assert_eq!(deserialized, minus_two_point_five);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_abs_should_work() {
|
||||
// normal
|
||||
assert_eq!(Fixed128::from_parts(1).saturating_abs(), Fixed128::from_parts(1));
|
||||
assert_eq!(Fixed128::from_parts(-1).saturating_abs(), Fixed128::from_parts(1));
|
||||
|
||||
// saturating
|
||||
assert_eq!(Fixed128::min_value().saturating_abs(), Fixed128::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_positive_negative_should_work() {
|
||||
let positive = Fixed128::from_parts(1);
|
||||
assert!(positive.is_positive());
|
||||
assert!(!positive.is_negative());
|
||||
|
||||
let negative = Fixed128::from_parts(-1);
|
||||
assert!(!negative.is_positive());
|
||||
assert!(negative.is_negative());
|
||||
|
||||
let zero = Fixed128::zero();
|
||||
assert!(!zero.is_positive());
|
||||
assert!(!zero.is_negative());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fmt_should_work() {
|
||||
let positive = Fixed128::from_parts(1000000000000000001);
|
||||
assert_eq!(format!("{:?}", positive), "Fixed128(1.000000000000000001)");
|
||||
let negative = Fixed128::from_parts(-1000000000000000001);
|
||||
assert_eq!(format!("{:?}", negative), "Fixed128(-1.000000000000000001)");
|
||||
|
||||
let positive_fractional = Fixed128::from_parts(1);
|
||||
assert_eq!(format!("{:?}", positive_fractional), "Fixed128(0.000000000000000001)");
|
||||
let negative_fractional = Fixed128::from_parts(-1);
|
||||
assert_eq!(format!("{:?}", negative_fractional), "Fixed128(-0.000000000000000001)");
|
||||
|
||||
let zero = Fixed128::zero();
|
||||
assert_eq!(format!("{:?}", zero), "Fixed128(0.000000000000000000)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_pow_should_work() {
|
||||
assert_eq!(Fixed128::from_natural(2).saturating_pow(0), Fixed128::from_natural(1));
|
||||
assert_eq!(Fixed128::from_natural(2).saturating_pow(1), Fixed128::from_natural(2));
|
||||
assert_eq!(Fixed128::from_natural(2).saturating_pow(2), Fixed128::from_natural(4));
|
||||
assert_eq!(Fixed128::from_natural(2).saturating_pow(3), Fixed128::from_natural(8));
|
||||
assert_eq!(Fixed128::from_natural(2).saturating_pow(50), Fixed128::from_natural(1125899906842624));
|
||||
|
||||
assert_eq!(Fixed128::from_natural(1).saturating_pow(1000), Fixed128::from_natural(1));
|
||||
assert_eq!(Fixed128::from_natural(-1).saturating_pow(1000), Fixed128::from_natural(1));
|
||||
assert_eq!(Fixed128::from_natural(-1).saturating_pow(1001), Fixed128::from_natural(-1));
|
||||
assert_eq!(Fixed128::from_natural(1).saturating_pow(usize::max_value()), Fixed128::from_natural(1));
|
||||
assert_eq!(Fixed128::from_natural(-1).saturating_pow(usize::max_value()), Fixed128::from_natural(-1));
|
||||
assert_eq!(Fixed128::from_natural(-1).saturating_pow(usize::max_value() - 1), Fixed128::from_natural(1));
|
||||
|
||||
assert_eq!(Fixed128::from_natural(114209).saturating_pow(4), Fixed128::from_natural(170137997018538053761));
|
||||
assert_eq!(Fixed128::from_natural(114209).saturating_pow(5), Fixed128::max_value());
|
||||
|
||||
assert_eq!(Fixed128::from_natural(1).saturating_pow(usize::max_value()), Fixed128::from_natural(1));
|
||||
assert_eq!(Fixed128::from_natural(0).saturating_pow(usize::max_value()), Fixed128::from_natural(0));
|
||||
assert_eq!(Fixed128::from_natural(2).saturating_pow(usize::max_value()), Fixed128::max_value());
|
||||
}
|
||||
}
|
||||
@@ -1,382 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use sp_std::{
|
||||
ops, prelude::*,
|
||||
convert::{TryFrom, TryInto},
|
||||
};
|
||||
use codec::{Encode, Decode};
|
||||
use crate::{
|
||||
Perbill,
|
||||
traits::{
|
||||
SaturatedConversion, CheckedSub, CheckedAdd, CheckedDiv, Bounded, UniqueSaturatedInto, Saturating
|
||||
}
|
||||
};
|
||||
|
||||
/// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036]
|
||||
/// with fixed point accuracy of one billion.
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Fixed64(i64);
|
||||
|
||||
/// The accuracy of the `Fixed64` type.
|
||||
const DIV: i64 = 1_000_000_000;
|
||||
|
||||
impl Fixed64 {
|
||||
/// creates self from a natural number.
|
||||
///
|
||||
/// Note that this might be lossy.
|
||||
pub fn from_natural(int: i64) -> Self {
|
||||
Self(int.saturating_mul(DIV))
|
||||
}
|
||||
|
||||
/// Return the accuracy of the type. Given that this function returns the value `X`, it means
|
||||
/// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`.
|
||||
pub fn accuracy() -> i64 {
|
||||
DIV
|
||||
}
|
||||
|
||||
/// Consume self and return the inner value.
|
||||
pub fn into_inner(self) -> i64 { self.0 }
|
||||
|
||||
/// Raw constructor. Equal to `parts / 1_000_000_000`.
|
||||
pub fn from_parts(parts: i64) -> Self {
|
||||
Self(parts)
|
||||
}
|
||||
|
||||
/// creates self from a rational number. Equal to `n/d`.
|
||||
///
|
||||
/// Note that this might be lossy.
|
||||
pub fn from_rational(n: i64, d: u64) -> Self {
|
||||
Self(
|
||||
(i128::from(n).saturating_mul(i128::from(DIV)) / i128::from(d).max(1))
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| Bounded::max_value())
|
||||
)
|
||||
}
|
||||
|
||||
/// Performs a saturated multiply and accumulate by unsigned number.
|
||||
///
|
||||
/// Returns a saturated `int + (self * int)`.
|
||||
pub fn saturated_multiply_accumulate<N>(self, int: N) -> N
|
||||
where
|
||||
N: TryFrom<u64> + From<u32> + UniqueSaturatedInto<u32> + Bounded + Clone + Saturating +
|
||||
ops::Rem<N, Output=N> + ops::Div<N, Output=N> + ops::Mul<N, Output=N> +
|
||||
ops::Add<N, Output=N>,
|
||||
{
|
||||
let div = DIV as u64;
|
||||
let positive = self.0 > 0;
|
||||
// safe to convert as absolute value.
|
||||
let parts = self.0.checked_abs().map(|v| v as u64).unwrap_or(i64::max_value() as u64 + 1);
|
||||
|
||||
|
||||
// will always fit.
|
||||
let natural_parts = parts / div;
|
||||
// might saturate.
|
||||
let natural_parts: N = natural_parts.saturated_into();
|
||||
// fractional parts can always fit into u32.
|
||||
let perbill_parts = (parts % div) as u32;
|
||||
|
||||
let n = int.clone().saturating_mul(natural_parts);
|
||||
let p = Perbill::from_parts(perbill_parts) * int.clone();
|
||||
|
||||
// everything that needs to be either added or subtracted from the original weight.
|
||||
let excess = n.saturating_add(p);
|
||||
|
||||
if positive {
|
||||
int.saturating_add(excess)
|
||||
} else {
|
||||
int.saturating_sub(excess)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_negative(&self) -> bool {
|
||||
self.0.is_negative()
|
||||
}
|
||||
}
|
||||
|
||||
impl Saturating for Fixed64 {
|
||||
fn saturating_add(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_add(rhs.0))
|
||||
}
|
||||
|
||||
fn saturating_mul(self, rhs: Self) -> Self {
|
||||
let a = self.0 as i128;
|
||||
let b = rhs.0 as i128;
|
||||
let res = a * b / DIV as i128;
|
||||
Self(res.saturated_into())
|
||||
}
|
||||
|
||||
fn saturating_sub(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_sub(rhs.0))
|
||||
}
|
||||
|
||||
fn saturating_pow(self, exp: usize) -> Self {
|
||||
if exp == 0 {
|
||||
return Self::from_natural(1);
|
||||
}
|
||||
|
||||
let exp = exp as u64;
|
||||
let msb_pos = 64 - exp.leading_zeros();
|
||||
|
||||
let mut result = Self::from_natural(1);
|
||||
let mut pow_val = self;
|
||||
for i in 0..msb_pos {
|
||||
if ((1 << i) & exp) > 0 {
|
||||
result = result.saturating_mul(pow_val);
|
||||
}
|
||||
pow_val = pow_val.saturating_mul(pow_val);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Use `Saturating` trait for safe addition.
|
||||
impl ops::Add for Fixed64 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Use `Saturating` trait for safe subtraction.
|
||||
impl ops::Sub for Fixed64 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Use `CheckedDiv` trait for safe division.
|
||||
impl ops::Div for Fixed64 {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
if rhs.0 == 0 {
|
||||
panic!("attempt to divide by zero");
|
||||
}
|
||||
let (n, d) = if rhs.0 < 0 {
|
||||
(-self.0, rhs.0.abs() as u64)
|
||||
} else {
|
||||
(self.0, rhs.0 as u64)
|
||||
};
|
||||
Fixed64::from_rational(n, d)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedSub for Fixed64 {
|
||||
fn checked_sub(&self, rhs: &Self) -> Option<Self> {
|
||||
self.0.checked_sub(rhs.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedAdd for Fixed64 {
|
||||
fn checked_add(&self, rhs: &Self) -> Option<Self> {
|
||||
self.0.checked_add(rhs.0).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedDiv for Fixed64 {
|
||||
fn checked_div(&self, rhs: &Self) -> Option<Self> {
|
||||
if rhs.0 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(*self / *rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_std::fmt::Debug for Fixed64 {
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
let integral = {
|
||||
let int = self.0 / DIV;
|
||||
let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" };
|
||||
format!("{}{}", signum_for_zero, int)
|
||||
};
|
||||
let fractional = format!("{:0>9}", (self.0 % DIV).abs());
|
||||
write!(f, "Fixed64({}.{})", integral, fractional)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn max() -> Fixed64 {
|
||||
Fixed64::from_parts(i64::max_value())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed64_semantics() {
|
||||
assert_eq!(Fixed64::from_rational(5, 2).0, 5 * 1_000_000_000 / 2);
|
||||
assert_eq!(Fixed64::from_rational(5, 2), Fixed64::from_rational(10, 4));
|
||||
assert_eq!(Fixed64::from_rational(5, 0), Fixed64::from_rational(5, 1));
|
||||
|
||||
// biggest value that can be created.
|
||||
assert_ne!(max(), Fixed64::from_natural(9_223_372_036));
|
||||
assert_eq!(max(), Fixed64::from_natural(9_223_372_037));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_64_growth_decrease_curve() {
|
||||
let test_set = vec![0u32, 1, 10, 1000, 1_000_000_000];
|
||||
|
||||
// negative (1/2)
|
||||
let mut fm = Fixed64::from_rational(-1, 2);
|
||||
test_set.clone().into_iter().for_each(|i| {
|
||||
assert_eq!(fm.saturated_multiply_accumulate(i) as i32, i as i32 - i as i32 / 2);
|
||||
});
|
||||
|
||||
// unit (1) multiplier
|
||||
fm = Fixed64::from_parts(0);
|
||||
test_set.clone().into_iter().for_each(|i| {
|
||||
assert_eq!(fm.saturated_multiply_accumulate(i), i);
|
||||
});
|
||||
|
||||
// i.5 multiplier
|
||||
fm = Fixed64::from_rational(1, 2);
|
||||
test_set.clone().into_iter().for_each(|i| {
|
||||
assert_eq!(fm.saturated_multiply_accumulate(i), i * 3 / 2);
|
||||
});
|
||||
|
||||
// dual multiplier
|
||||
fm = Fixed64::from_rational(1, 1);
|
||||
test_set.clone().into_iter().for_each(|i| {
|
||||
assert_eq!(fm.saturated_multiply_accumulate(i), i * 2);
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! saturating_mul_acc_test {
|
||||
($num_type:tt) => {
|
||||
assert_eq!(
|
||||
Fixed64::from_rational(100, 1).saturated_multiply_accumulate(10 as $num_type),
|
||||
1010,
|
||||
);
|
||||
assert_eq!(
|
||||
Fixed64::from_rational(100, 2).saturated_multiply_accumulate(10 as $num_type),
|
||||
510,
|
||||
);
|
||||
assert_eq!(
|
||||
Fixed64::from_rational(100, 3).saturated_multiply_accumulate(0 as $num_type),
|
||||
0,
|
||||
);
|
||||
assert_eq!(
|
||||
Fixed64::from_rational(5, 1).saturated_multiply_accumulate($num_type::max_value()),
|
||||
$num_type::max_value()
|
||||
);
|
||||
assert_eq!(
|
||||
max().saturated_multiply_accumulate($num_type::max_value()),
|
||||
$num_type::max_value()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed64_multiply_accumulate_works() {
|
||||
saturating_mul_acc_test!(u32);
|
||||
saturating_mul_acc_test!(u64);
|
||||
saturating_mul_acc_test!(u128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_works() {
|
||||
let a = Fixed64::from_rational(12, 10);
|
||||
let b = Fixed64::from_rational(10, 1);
|
||||
assert_eq!(a / b, Fixed64::from_rational(12, 100));
|
||||
|
||||
let a = Fixed64::from_rational(12, 10);
|
||||
let b = Fixed64::from_rational(1, 100);
|
||||
assert_eq!(a / b, Fixed64::from_rational(120, 1));
|
||||
|
||||
let a = Fixed64::from_rational(12, 100);
|
||||
let b = Fixed64::from_rational(10, 1);
|
||||
assert_eq!(a / b, Fixed64::from_rational(12, 1000));
|
||||
|
||||
let a = Fixed64::from_rational(12, 100);
|
||||
let b = Fixed64::from_rational(1, 100);
|
||||
assert_eq!(a / b, Fixed64::from_rational(12, 1));
|
||||
|
||||
let a = Fixed64::from_rational(-12, 10);
|
||||
let b = Fixed64::from_rational(10, 1);
|
||||
assert_eq!(a / b, Fixed64::from_rational(-12, 100));
|
||||
|
||||
let a = Fixed64::from_rational(12, 10);
|
||||
let b = Fixed64::from_rational(-10, 1);
|
||||
assert_eq!(a / b, Fixed64::from_rational(-12, 100));
|
||||
|
||||
let a = Fixed64::from_rational(-12, 10);
|
||||
let b = Fixed64::from_rational(-10, 1);
|
||||
assert_eq!(a / b, Fixed64::from_rational(12, 100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "attempt to divide by zero")]
|
||||
fn div_zero() {
|
||||
let a = Fixed64::from_rational(12, 10);
|
||||
let b = Fixed64::from_natural(0);
|
||||
let _ = a / b;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_zero() {
|
||||
let a = Fixed64::from_rational(12, 10);
|
||||
let b = Fixed64::from_natural(0);
|
||||
assert_eq!(a.checked_div(&b), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_non_zero() {
|
||||
let a = Fixed64::from_rational(12, 10);
|
||||
let b = Fixed64::from_rational(1, 100);
|
||||
assert_eq!(a.checked_div(&b), Some(Fixed64::from_rational(120, 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_mul_should_work() {
|
||||
assert_eq!(Fixed64::from_natural(100).saturating_mul(Fixed64::from_natural(100)), Fixed64::from_natural(10000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_pow_should_work() {
|
||||
assert_eq!(Fixed64::from_natural(2).saturating_pow(0), Fixed64::from_natural(1));
|
||||
assert_eq!(Fixed64::from_natural(2).saturating_pow(1), Fixed64::from_natural(2));
|
||||
assert_eq!(Fixed64::from_natural(2).saturating_pow(2), Fixed64::from_natural(4));
|
||||
assert_eq!(Fixed64::from_natural(2).saturating_pow(3), Fixed64::from_natural(8));
|
||||
assert_eq!(Fixed64::from_natural(2).saturating_pow(20), Fixed64::from_natural(1048576));
|
||||
|
||||
assert_eq!(Fixed64::from_natural(1).saturating_pow(1000), Fixed64::from_natural(1));
|
||||
assert_eq!(Fixed64::from_natural(-1).saturating_pow(1000), Fixed64::from_natural(1));
|
||||
assert_eq!(Fixed64::from_natural(-1).saturating_pow(1001), Fixed64::from_natural(-1));
|
||||
assert_eq!(Fixed64::from_natural(1).saturating_pow(usize::max_value()), Fixed64::from_natural(1));
|
||||
assert_eq!(Fixed64::from_natural(-1).saturating_pow(usize::max_value()), Fixed64::from_natural(-1));
|
||||
assert_eq!(Fixed64::from_natural(-1).saturating_pow(usize::max_value() - 1), Fixed64::from_natural(1));
|
||||
|
||||
assert_eq!(Fixed64::from_natural(309).saturating_pow(4), Fixed64::from_natural(9_116_621_361));
|
||||
assert_eq!(Fixed64::from_natural(309).saturating_pow(5), Fixed64::from_parts(i64::max_value()));
|
||||
|
||||
assert_eq!(Fixed64::from_natural(1).saturating_pow(usize::max_value()), Fixed64::from_natural(1));
|
||||
assert_eq!(Fixed64::from_natural(0).saturating_pow(usize::max_value()), Fixed64::from_natural(0));
|
||||
assert_eq!(Fixed64::from_natural(2).saturating_pow(usize::max_value()), Fixed64::from_parts(i64::max_value()));
|
||||
}
|
||||
}
|
||||
@@ -37,12 +37,10 @@ pub mod biguint;
|
||||
pub mod helpers_128bit;
|
||||
pub mod traits;
|
||||
mod per_things;
|
||||
mod fixed64;
|
||||
mod fixed128;
|
||||
mod fixed;
|
||||
mod rational128;
|
||||
|
||||
pub use fixed64::Fixed64;
|
||||
pub use fixed128::Fixed128;
|
||||
pub use fixed::{FixedPointNumber, Fixed64, Fixed128};
|
||||
pub use per_things::{PerThing, Percent, PerU16, Permill, Perbill, Perquintill};
|
||||
pub use rational128::Rational128;
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ use sp_std::{self, convert::{TryFrom, TryInto}};
|
||||
use codec::HasCompact;
|
||||
pub use integer_sqrt::IntegerSquareRoot;
|
||||
pub use num_traits::{
|
||||
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
|
||||
CheckedShl, CheckedShr, checked_pow
|
||||
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedNeg,
|
||||
CheckedShl, CheckedShr, checked_pow, Signed
|
||||
};
|
||||
use sp_std::ops::{
|
||||
Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign,
|
||||
|
||||
@@ -72,7 +72,7 @@ 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,
|
||||
PerThing, traits::SaturatedConversion, FixedPointNumber,
|
||||
};
|
||||
/// Re-export 128 bit helpers.
|
||||
pub use sp_arithmetic::helpers_128bit;
|
||||
|
||||
Reference in New Issue
Block a user