From cec8b35d2e6dae96b1ce2995e4a1f79a1391af67 Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 22 Apr 2022 16:40:53 +0200 Subject: [PATCH 1/7] Decimal: Implement division by decimal --- packages/std/src/math/decimal.rs | 103 ++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index 5b15a9c31a..e2a52f434b 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -437,6 +437,23 @@ impl Mul for Decimal { } } +impl Div for Decimal { + type Output = Self; + + fn div(self, other: Self) -> Self { + // Decimal division is just multiplication of inverted second argument + + let inverted = other.inv().expect("attempt to divide by zero"); + let result_as_uint256 = self.numerator().full_mul(inverted.numerator()) + / Uint256::from_uint128(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" + match result_as_uint256.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to divide with overflow"), + } + } +} +forward_ref_binop!(impl Div, div for Decimal, Decimal); + impl Div for Decimal { type Output = Self; @@ -1170,7 +1187,7 @@ mod tests { (Decimal::percent(10), Decimal::zero()), (Decimal::percent(10), Decimal::percent(5)), (Decimal::MAX, Decimal::one()), - (Decimal::MAX / 2u128.into(), Decimal::percent(200)), + // (Decimal::MAX / 2u128.into(), Decimal::percent(200)), (Decimal::permille(6), Decimal::permille(13)), ]; @@ -1192,6 +1209,90 @@ mod tests { ); } + #[test] + #[allow(clippy::op_ref)] + fn decimal_implements_div() { + let one = Decimal::one(); + let two = one + one; + let half = Decimal::percent(50); + + fn dec(input: &str) -> Decimal { + Decimal::from_str(input).unwrap() + } + + // 1/x and x/1 + assert_eq!(one / Decimal::percent(1), Decimal::percent(10_000)); + assert_eq!(one / Decimal::percent(10), Decimal::percent(1_000)); + assert_eq!(one / Decimal::percent(100), Decimal::percent(100)); + assert_eq!(one / Decimal::percent(1000), Decimal::percent(10)); + assert_eq!(Decimal::percent(1) / one, Decimal::percent(1)); + assert_eq!(Decimal::percent(10) / one, Decimal::percent(10)); + assert_eq!(Decimal::percent(100) / one, Decimal::percent(100)); + assert_eq!(Decimal::percent(1000) / one, Decimal::percent(1000)); + + // double + assert_eq!(two / Decimal::percent(1), Decimal::percent(20_000)); + assert_eq!(two / Decimal::percent(10), Decimal::percent(2_000)); + assert_eq!(two / Decimal::percent(100), Decimal::percent(200)); + assert_eq!(two / Decimal::percent(1000), Decimal::percent(20)); + assert_eq!(Decimal::percent(1) / two, dec("0.005")); + assert_eq!(Decimal::percent(10) / two, Decimal::percent(5)); + assert_eq!(Decimal::percent(100) / two, Decimal::percent(50)); + assert_eq!(Decimal::percent(1000) / two, Decimal::percent(500)); + + // half + assert_eq!(half / Decimal::percent(1), Decimal::percent(5_000)); + assert_eq!(half / Decimal::percent(10), Decimal::percent(500)); + assert_eq!(half / Decimal::percent(100), Decimal::percent(50)); + assert_eq!(half / Decimal::percent(1000), Decimal::percent(5)); + assert_eq!(Decimal::percent(1) / half, Decimal::percent(2)); + assert_eq!(Decimal::percent(10) / half, Decimal::percent(20)); + assert_eq!(Decimal::percent(100) / half, Decimal::percent(200)); + assert_eq!(Decimal::percent(1000) / half, Decimal::percent(2000)); + + // Move left + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.000000000000000008")); + assert_eq!(dec("10") / a, dec("0.000000000000000080")); + assert_eq!(dec("100") / a, dec("0.000000000000000800")); + assert_eq!(dec("1000") / a, dec("0.000000000000008000")); + assert_eq!(dec("1000000") / a, dec("0.000000000008000000")); + assert_eq!(dec("1000000000") / a, dec("0.000000008000000000")); + assert_eq!(dec("1000000000000") / a, dec("0.000008000000000000")); + assert_eq!(dec("1000000000000000") / a, dec("0.008000000000000000")); + assert_eq!(dec("1000000000000000000") / a, dec("8")); + + // Move right + let max = dec("0.123127726548762582"); + assert_eq!(max / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(max / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(max / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(max / dec("0.001"), dec("123.127726548762582")); + assert_eq!(max / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(max / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(max / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(max / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(max / dec("0.000000000000000001"), dec("123127726548762582")); + + // works for refs + let a = Decimal::percent(100); + let b = Decimal::percent(20); + let expected = Decimal::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + #[test] // in this test the Decimal is on the right fn uint128_decimal_multiply() { From 6c03559819b40436575f1674b0dfc9eed3cb7371 Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 22 Apr 2022 17:22:30 +0200 Subject: [PATCH 2/7] Decimal: Implement DivAssign --- packages/std/src/math/decimal.rs | 63 +++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index e2a52f434b..4d40805429 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -445,7 +445,7 @@ impl Div for Decimal { let inverted = other.inv().expect("attempt to divide by zero"); let result_as_uint256 = self.numerator().full_mul(inverted.numerator()) - / Uint256::from_uint128(Self::DECIMAL_FRACTIONAL); // from_uint128 is a const method and should be "free" + / Uint256::from_uint128(Self::DECIMAL_FRACTIONAL); match result_as_uint256.try_into() { Ok(result) => Self(result), Err(_) => panic!("attempt to divide with overflow"), @@ -454,6 +454,13 @@ impl Div for Decimal { } forward_ref_binop!(impl Div, div for Decimal, Decimal); +impl DivAssign for Decimal { + fn div_assign(&mut self, rhs: Decimal) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Decimal, Decimal); + impl Div for Decimal { type Output = Self; @@ -1187,7 +1194,7 @@ mod tests { (Decimal::percent(10), Decimal::zero()), (Decimal::percent(10), Decimal::percent(5)), (Decimal::MAX, Decimal::one()), - // (Decimal::MAX / 2u128.into(), Decimal::percent(200)), + (Decimal::MAX / Uint128::new(2), Decimal::percent(200)), (Decimal::permille(6), Decimal::permille(13)), ]; @@ -1225,6 +1232,7 @@ mod tests { assert_eq!(one / Decimal::percent(10), Decimal::percent(1_000)); assert_eq!(one / Decimal::percent(100), Decimal::percent(100)); assert_eq!(one / Decimal::percent(1000), Decimal::percent(10)); + assert_eq!(Decimal::percent(0) / one, Decimal::percent(0)); assert_eq!(Decimal::percent(1) / one, Decimal::percent(1)); assert_eq!(Decimal::percent(10) / one, Decimal::percent(10)); assert_eq!(Decimal::percent(100) / one, Decimal::percent(100)); @@ -1235,6 +1243,7 @@ mod tests { assert_eq!(two / Decimal::percent(10), Decimal::percent(2_000)); assert_eq!(two / Decimal::percent(100), Decimal::percent(200)); assert_eq!(two / Decimal::percent(1000), Decimal::percent(20)); + assert_eq!(Decimal::percent(0) / two, Decimal::percent(0)); assert_eq!(Decimal::percent(1) / two, dec("0.005")); assert_eq!(Decimal::percent(10) / two, Decimal::percent(5)); assert_eq!(Decimal::percent(100) / two, Decimal::percent(50)); @@ -1245,12 +1254,13 @@ mod tests { assert_eq!(half / Decimal::percent(10), Decimal::percent(500)); assert_eq!(half / Decimal::percent(100), Decimal::percent(50)); assert_eq!(half / Decimal::percent(1000), Decimal::percent(5)); + assert_eq!(Decimal::percent(0) / half, Decimal::percent(0)); assert_eq!(Decimal::percent(1) / half, Decimal::percent(2)); assert_eq!(Decimal::percent(10) / half, Decimal::percent(20)); assert_eq!(Decimal::percent(100) / half, Decimal::percent(200)); assert_eq!(Decimal::percent(1000) / half, Decimal::percent(2000)); - // Move left + // Move right let a = dec("123127726548762582"); assert_eq!(a / dec("1"), dec("123127726548762582")); assert_eq!(a / dec("10"), dec("12312772654876258.2")); @@ -1271,17 +1281,17 @@ mod tests { assert_eq!(dec("1000000000000000") / a, dec("0.008000000000000000")); assert_eq!(dec("1000000000000000000") / a, dec("8")); - // Move right - let max = dec("0.123127726548762582"); - assert_eq!(max / dec("1.0"), dec("0.123127726548762582")); - assert_eq!(max / dec("0.1"), dec("1.23127726548762582")); - assert_eq!(max / dec("0.01"), dec("12.3127726548762582")); - assert_eq!(max / dec("0.001"), dec("123.127726548762582")); - assert_eq!(max / dec("0.000001"), dec("123127.726548762582")); - assert_eq!(max / dec("0.000000001"), dec("123127726.548762582")); - assert_eq!(max / dec("0.000000000001"), dec("123127726548.762582")); - assert_eq!(max / dec("0.000000000000001"), dec("123127726548762.582")); - assert_eq!(max / dec("0.000000000000000001"), dec("123127726548762582")); + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); // works for refs let a = Decimal::percent(100); @@ -1293,6 +1303,31 @@ mod tests { assert_eq!(&a / &b, expected); } + #[test] + fn decimal_div_assign_works() { + let mut a = Decimal::percent(15); + a /= Decimal::percent(20); + assert_eq!(a, Decimal::percent(75)); + + // works for refs + let mut a = Decimal::percent(50); + let b = Decimal::percent(20); + a /= &b; + assert_eq!(a, Decimal::percent(250)); + } + + #[test] + #[should_panic(expected = "attempt to divide with overflow")] + fn decimal_div_overflow_panics() { + let _value = Decimal::MAX / Decimal::percent(10); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn decimal_div_by_zero_panics() { + let _value = Decimal::one() / Decimal::zero(); + } + #[test] // in this test the Decimal is on the right fn uint128_decimal_multiply() { From 286aab3b5a5ec702b28754175e315f191eaa1df9 Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 22 Apr 2022 17:44:03 +0200 Subject: [PATCH 3/7] Decimal256: Implement Div/DivAssign --- packages/std/src/math/decimal.rs | 76 +++++++-------- packages/std/src/math/decimal256.rs | 141 +++++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 39 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index 4d40805429..16ab3f56d1 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -1216,6 +1216,44 @@ mod tests { ); } + #[test] + // in this test the Decimal is on the right + fn uint128_decimal_multiply() { + // a*b + let left = Uint128::new(300); + let right = Decimal::one() + Decimal::percent(50); // 1.5 + assert_eq!(left * right, Uint128::new(450)); + + // a*0 + let left = Uint128::new(300); + let right = Decimal::zero(); + assert_eq!(left * right, Uint128::new(0)); + + // 0*a + let left = Uint128::new(0); + let right = Decimal::one() + Decimal::percent(50); // 1.5 + assert_eq!(left * right, Uint128::new(0)); + } + + #[test] + // in this test the Decimal is on the left + fn decimal_uint128_multiply() { + // a*b + let left = Decimal::one() + Decimal::percent(50); // 1.5 + let right = Uint128::new(300); + assert_eq!(left * right, Uint128::new(450)); + + // 0*a + let left = Decimal::zero(); + let right = Uint128::new(300); + assert_eq!(left * right, Uint128::new(0)); + + // a*0 + let left = Decimal::one() + Decimal::percent(50); // 1.5 + let right = Uint128::new(0); + assert_eq!(left * right, Uint128::new(0)); + } + #[test] #[allow(clippy::op_ref)] fn decimal_implements_div() { @@ -1328,44 +1366,6 @@ mod tests { let _value = Decimal::one() / Decimal::zero(); } - #[test] - // in this test the Decimal is on the right - fn uint128_decimal_multiply() { - // a*b - let left = Uint128::new(300); - let right = Decimal::one() + Decimal::percent(50); // 1.5 - assert_eq!(left * right, Uint128::new(450)); - - // a*0 - let left = Uint128::new(300); - let right = Decimal::zero(); - assert_eq!(left * right, Uint128::new(0)); - - // 0*a - let left = Uint128::new(0); - let right = Decimal::one() + Decimal::percent(50); // 1.5 - assert_eq!(left * right, Uint128::new(0)); - } - - #[test] - // in this test the Decimal is on the left - fn decimal_uint128_multiply() { - // a*b - let left = Decimal::one() + Decimal::percent(50); // 1.5 - let right = Uint128::new(300); - assert_eq!(left * right, Uint128::new(450)); - - // 0*a - let left = Decimal::zero(); - let right = Uint128::new(300); - assert_eq!(left * right, Uint128::new(0)); - - // a*0 - let left = Decimal::one() + Decimal::percent(50); // 1.5 - let right = Uint128::new(0); - assert_eq!(left * right, Uint128::new(0)); - } - #[test] fn decimal_uint128_division() { // a/b diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index a7898203ba..02da0dbcac 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -449,6 +449,30 @@ impl Mul for Decimal256 { } } +impl Div for Decimal256 { + type Output = Self; + + fn div(self, other: Self) -> Self { + // Decimal division is just multiplication of inverted second argument + + let inverted = other.inv().expect("attempt to divide by zero"); + let result_as_uint512 = self.numerator().full_mul(inverted.numerator()) + / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); + match result_as_uint512.try_into() { + Ok(result) => Self(result), + Err(_) => panic!("attempt to divide with overflow"), + } + } +} +forward_ref_binop!(impl Div, div for Decimal256, Decimal256); + +impl DivAssign for Decimal256 { + fn div_assign(&mut self, rhs: Decimal256) { + *self = *self / rhs; + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Decimal256, Decimal256); + impl Div for Decimal256 { type Output = Self; @@ -1258,7 +1282,10 @@ mod tests { (Decimal256::percent(10), Decimal256::zero()), (Decimal256::percent(10), Decimal256::percent(5)), (Decimal256::MAX, Decimal256::one()), - (Decimal256::MAX / 2u128.into(), Decimal256::percent(200)), + ( + Decimal256::MAX / Uint256::from_uint128(2u128.into()), + Decimal256::percent(200), + ), (Decimal256::permille(6), Decimal256::permille(13)), ]; @@ -1318,6 +1345,118 @@ mod tests { assert_eq!(left * right, Uint256::from(0u128)); } + #[test] + #[allow(clippy::op_ref)] + fn decimal256_implements_div() { + let one = Decimal256::one(); + let two = one + one; + let half = Decimal256::percent(50); + + fn dec(input: &str) -> Decimal256 { + Decimal256::from_str(input).unwrap() + } + + // 1/x and x/1 + assert_eq!(one / Decimal256::percent(1), Decimal256::percent(10_000)); + assert_eq!(one / Decimal256::percent(10), Decimal256::percent(1_000)); + assert_eq!(one / Decimal256::percent(100), Decimal256::percent(100)); + assert_eq!(one / Decimal256::percent(1000), Decimal256::percent(10)); + assert_eq!(Decimal256::percent(0) / one, Decimal256::percent(0)); + assert_eq!(Decimal256::percent(1) / one, Decimal256::percent(1)); + assert_eq!(Decimal256::percent(10) / one, Decimal256::percent(10)); + assert_eq!(Decimal256::percent(100) / one, Decimal256::percent(100)); + assert_eq!(Decimal256::percent(1000) / one, Decimal256::percent(1000)); + + // double + assert_eq!(two / Decimal256::percent(1), Decimal256::percent(20_000)); + assert_eq!(two / Decimal256::percent(10), Decimal256::percent(2_000)); + assert_eq!(two / Decimal256::percent(100), Decimal256::percent(200)); + assert_eq!(two / Decimal256::percent(1000), Decimal256::percent(20)); + assert_eq!(Decimal256::percent(0) / two, Decimal256::percent(0)); + assert_eq!(Decimal256::percent(1) / two, dec("0.005")); + assert_eq!(Decimal256::percent(10) / two, Decimal256::percent(5)); + assert_eq!(Decimal256::percent(100) / two, Decimal256::percent(50)); + assert_eq!(Decimal256::percent(1000) / two, Decimal256::percent(500)); + + // half + assert_eq!(half / Decimal256::percent(1), Decimal256::percent(5_000)); + assert_eq!(half / Decimal256::percent(10), Decimal256::percent(500)); + assert_eq!(half / Decimal256::percent(100), Decimal256::percent(50)); + assert_eq!(half / Decimal256::percent(1000), Decimal256::percent(5)); + assert_eq!(Decimal256::percent(0) / half, Decimal256::percent(0)); + assert_eq!(Decimal256::percent(1) / half, Decimal256::percent(2)); + assert_eq!(Decimal256::percent(10) / half, Decimal256::percent(20)); + assert_eq!(Decimal256::percent(100) / half, Decimal256::percent(200)); + assert_eq!(Decimal256::percent(1000) / half, Decimal256::percent(2000)); + + // Move right + let a = dec("123127726548762582"); + assert_eq!(a / dec("1"), dec("123127726548762582")); + assert_eq!(a / dec("10"), dec("12312772654876258.2")); + assert_eq!(a / dec("100"), dec("1231277265487625.82")); + assert_eq!(a / dec("1000"), dec("123127726548762.582")); + assert_eq!(a / dec("1000000"), dec("123127726548.762582")); + assert_eq!(a / dec("1000000000"), dec("123127726.548762582")); + assert_eq!(a / dec("1000000000000"), dec("123127.726548762582")); + assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); + assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); + assert_eq!(dec("1") / a, dec("0.000000000000000008")); + assert_eq!(dec("10") / a, dec("0.000000000000000080")); + assert_eq!(dec("100") / a, dec("0.000000000000000800")); + assert_eq!(dec("1000") / a, dec("0.000000000000008000")); + assert_eq!(dec("1000000") / a, dec("0.000000000008000000")); + assert_eq!(dec("1000000000") / a, dec("0.000000008000000000")); + assert_eq!(dec("1000000000000") / a, dec("0.000008000000000000")); + assert_eq!(dec("1000000000000000") / a, dec("0.008000000000000000")); + assert_eq!(dec("1000000000000000000") / a, dec("8")); + + // Move left + let a = dec("0.123127726548762582"); + assert_eq!(a / dec("1.0"), dec("0.123127726548762582")); + assert_eq!(a / dec("0.1"), dec("1.23127726548762582")); + assert_eq!(a / dec("0.01"), dec("12.3127726548762582")); + assert_eq!(a / dec("0.001"), dec("123.127726548762582")); + assert_eq!(a / dec("0.000001"), dec("123127.726548762582")); + assert_eq!(a / dec("0.000000001"), dec("123127726.548762582")); + assert_eq!(a / dec("0.000000000001"), dec("123127726548.762582")); + assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); + assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + + // works for refs + let a = Decimal256::percent(100); + let b = Decimal256::percent(20); + let expected = Decimal256::percent(500); + assert_eq!(a / b, expected); + assert_eq!(&a / b, expected); + assert_eq!(a / &b, expected); + assert_eq!(&a / &b, expected); + } + + #[test] + fn decimal256_div_assign_works() { + let mut a = Decimal256::percent(15); + a /= Decimal256::percent(20); + assert_eq!(a, Decimal256::percent(75)); + + // works for refs + let mut a = Decimal256::percent(50); + let b = Decimal256::percent(20); + a /= &b; + assert_eq!(a, Decimal256::percent(250)); + } + + #[test] + #[should_panic(expected = "attempt to divide with overflow")] + fn decimal256_div_overflow_panics() { + let _value = Decimal256::MAX / Decimal256::percent(10); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn decimal256_div_by_zero_panics() { + let _value = Decimal256::one() / Decimal256::zero(); + } + #[test] fn decimal256_uint128_division() { // a/b From 2280f2825c053bbb8fc26c9f7de3d7e564d682df Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 22 Apr 2022 17:45:01 +0200 Subject: [PATCH 4/7] Update changelog --- CHANGELOG.md | 1 + packages/std/src/math/decimal.rs | 12 ++++-------- packages/std/src/math/decimal256.rs | 12 ++++-------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 196ff06fad..4d15cd5830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to - cosmwasm-std: Implement `checked_multiply_ratio` for `Uint64`/`Uint128`/`Uint256` - cosmwasm-std: Implement `checked_from_ratio` for `Decimal`/`Decimal256` +- cosmwasm-std: Implement `Div`/`DivAssign` for `Decimal`/`Decimal256`. - cosmwasm-vm: Add feature `allow_interface_version_7` to run CosmWasm 0.16 contracts in modern hosts. Be careful if you consider using this! diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index 16ab3f56d1..45808f90bb 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -529,6 +529,10 @@ mod tests { use super::*; use crate::{from_slice, to_vec}; + fn dec(input: &str) -> Decimal { + Decimal::from_str(input).unwrap() + } + #[test] fn decimal_new() { let expected = Uint128::from(300u128); @@ -1092,10 +1096,6 @@ mod tests { assert_eq!(Decimal::percent(100) * half, Decimal::percent(50)); assert_eq!(Decimal::percent(1000) * half, Decimal::percent(500)); - fn dec(input: &str) -> Decimal { - Decimal::from_str(input).unwrap() - } - // Move left let a = dec("123.127726548762582"); assert_eq!(a * dec("1"), dec("123.127726548762582")); @@ -1261,10 +1261,6 @@ mod tests { let two = one + one; let half = Decimal::percent(50); - fn dec(input: &str) -> Decimal { - Decimal::from_str(input).unwrap() - } - // 1/x and x/1 assert_eq!(one / Decimal::percent(1), Decimal::percent(10_000)); assert_eq!(one / Decimal::percent(10), Decimal::percent(1_000)); diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index 02da0dbcac..d67e019f37 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -542,6 +542,10 @@ mod tests { use crate::errors::StdError; use crate::{from_slice, to_vec}; + fn dec(input: &str) -> Decimal256 { + Decimal256::from_str(input).unwrap() + } + #[test] fn decimal256_new() { let expected = Uint256::from(300u128); @@ -1180,10 +1184,6 @@ mod tests { assert_eq!(Decimal256::percent(100) * half, Decimal256::percent(50)); assert_eq!(Decimal256::percent(1000) * half, Decimal256::percent(500)); - fn dec(input: &str) -> Decimal256 { - Decimal256::from_str(input).unwrap() - } - // Move left let a = dec("123.127726548762582"); assert_eq!(a * dec("1"), dec("123.127726548762582")); @@ -1352,10 +1352,6 @@ mod tests { let two = one + one; let half = Decimal256::percent(50); - fn dec(input: &str) -> Decimal256 { - Decimal256::from_str(input).unwrap() - } - // 1/x and x/1 assert_eq!(one / Decimal256::percent(1), Decimal256::percent(10_000)); assert_eq!(one / Decimal256::percent(10), Decimal256::percent(1_000)); From 8fde89c60009653e4b555b5fa8ef36862f8b540e Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Mon, 25 Apr 2022 11:40:36 +0200 Subject: [PATCH 5/7] Decimal: improve div implementation --- packages/std/src/math/decimal.rs | 33 +++++++++++++---------------- packages/std/src/math/decimal256.rs | 33 +++++++++++++---------------- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index 45808f90bb..14037d3858 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -441,15 +441,7 @@ impl Div for Decimal { type Output = Self; fn div(self, other: Self) -> Self { - // Decimal division is just multiplication of inverted second argument - - let inverted = other.inv().expect("attempt to divide by zero"); - let result_as_uint256 = self.numerator().full_mul(inverted.numerator()) - / Uint256::from_uint128(Self::DECIMAL_FRACTIONAL); - match result_as_uint256.try_into() { - Ok(result) => Self(result), - Err(_) => panic!("attempt to divide with overflow"), - } + Decimal::from_ratio(self.numerator(), other.numerator()) } } forward_ref_binop!(impl Div, div for Decimal, Decimal); @@ -1306,14 +1298,14 @@ mod tests { assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); assert_eq!(dec("1") / a, dec("0.000000000000000008")); - assert_eq!(dec("10") / a, dec("0.000000000000000080")); - assert_eq!(dec("100") / a, dec("0.000000000000000800")); - assert_eq!(dec("1000") / a, dec("0.000000000000008000")); - assert_eq!(dec("1000000") / a, dec("0.000000000008000000")); - assert_eq!(dec("1000000000") / a, dec("0.000000008000000000")); - assert_eq!(dec("1000000000000") / a, dec("0.000008000000000000")); - assert_eq!(dec("1000000000000000") / a, dec("0.008000000000000000")); - assert_eq!(dec("1000000000000000000") / a, dec("8")); + assert_eq!(dec("10") / a, dec("0.000000000000000081")); + assert_eq!(dec("100") / a, dec("0.000000000000000812")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); // Move left let a = dec("0.123127726548762582"); @@ -1327,6 +1319,11 @@ mod tests { assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + assert_eq!( + Decimal::percent(15) / Decimal::percent(60), + Decimal::percent(25) + ); + // works for refs let a = Decimal::percent(100); let b = Decimal::percent(20); @@ -1357,7 +1354,7 @@ mod tests { } #[test] - #[should_panic(expected = "attempt to divide by zero")] + #[should_panic(expected = "Denominator must not be zero")] fn decimal_div_by_zero_panics() { let _value = Decimal::one() / Decimal::zero(); } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index d67e019f37..187d7417aa 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -453,15 +453,7 @@ impl Div for Decimal256 { type Output = Self; fn div(self, other: Self) -> Self { - // Decimal division is just multiplication of inverted second argument - - let inverted = other.inv().expect("attempt to divide by zero"); - let result_as_uint512 = self.numerator().full_mul(inverted.numerator()) - / Uint512::from_uint256(Self::DECIMAL_FRACTIONAL); - match result_as_uint512.try_into() { - Ok(result) => Self(result), - Err(_) => panic!("attempt to divide with overflow"), - } + Decimal256::from_ratio(self.numerator(), other.numerator()) } } forward_ref_binop!(impl Div, div for Decimal256, Decimal256); @@ -1397,14 +1389,14 @@ mod tests { assert_eq!(a / dec("1000000000000000"), dec("123.127726548762582")); assert_eq!(a / dec("1000000000000000000"), dec("0.123127726548762582")); assert_eq!(dec("1") / a, dec("0.000000000000000008")); - assert_eq!(dec("10") / a, dec("0.000000000000000080")); - assert_eq!(dec("100") / a, dec("0.000000000000000800")); - assert_eq!(dec("1000") / a, dec("0.000000000000008000")); - assert_eq!(dec("1000000") / a, dec("0.000000000008000000")); - assert_eq!(dec("1000000000") / a, dec("0.000000008000000000")); - assert_eq!(dec("1000000000000") / a, dec("0.000008000000000000")); - assert_eq!(dec("1000000000000000") / a, dec("0.008000000000000000")); - assert_eq!(dec("1000000000000000000") / a, dec("8")); + assert_eq!(dec("10") / a, dec("0.000000000000000081")); + assert_eq!(dec("100") / a, dec("0.000000000000000812")); + assert_eq!(dec("1000") / a, dec("0.000000000000008121")); + assert_eq!(dec("1000000") / a, dec("0.000000000008121647")); + assert_eq!(dec("1000000000") / a, dec("0.000000008121647560")); + assert_eq!(dec("1000000000000") / a, dec("0.000008121647560868")); + assert_eq!(dec("1000000000000000") / a, dec("0.008121647560868164")); + assert_eq!(dec("1000000000000000000") / a, dec("8.121647560868164773")); // Move left let a = dec("0.123127726548762582"); @@ -1418,6 +1410,11 @@ mod tests { assert_eq!(a / dec("0.000000000000001"), dec("123127726548762.582")); assert_eq!(a / dec("0.000000000000000001"), dec("123127726548762582")); + assert_eq!( + Decimal256::percent(15) / Decimal256::percent(60), + Decimal256::percent(25) + ); + // works for refs let a = Decimal256::percent(100); let b = Decimal256::percent(20); @@ -1448,7 +1445,7 @@ mod tests { } #[test] - #[should_panic(expected = "attempt to divide by zero")] + #[should_panic(expected = "Denominator must not be zero")] fn decimal256_div_by_zero_panics() { let _value = Decimal256::one() / Decimal256::zero(); } From 557d274d47e40d8735ef6f38a2c7d9e0997fc3fb Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Mon, 25 Apr 2022 16:23:59 +0200 Subject: [PATCH 6/7] Decimal: Adjust div overflow error message in test to new implementation --- packages/std/src/math/decimal.rs | 2 +- packages/std/src/math/decimal256.rs | 2 +- packages/std/src/math/mod.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index 14037d3858..220ada17ec 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -1348,7 +1348,7 @@ mod tests { } #[test] - #[should_panic(expected = "attempt to divide with overflow")] + #[should_panic(expected = "Multiplication overflow")] fn decimal_div_overflow_panics() { let _value = Decimal::MAX / Decimal::percent(10); } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index 187d7417aa..736f8a5350 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -1439,7 +1439,7 @@ mod tests { } #[test] - #[should_panic(expected = "attempt to divide with overflow")] + #[should_panic(expected = "Multiplication overflow")] fn decimal256_div_overflow_panics() { let _value = Decimal256::MAX / Decimal256::percent(10); } diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 2190d0dccc..031999934e 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -70,10 +70,10 @@ mod tests { + Mul<&'a Self> + MulAssign + MulAssign<&'a Self> - // + Div - // + Div<&'a Self> - // + DivAssign - // + DivAssign<&'a Self> + + Div + + Div<&'a Self> + + DivAssign + + DivAssign<&'a Self> // + Rem // + Rem<&'a Self> // + RemAssign From d6d07b44a4502d06eec15e0b17e3b5b585ac2f2d Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Sat, 30 Apr 2022 08:49:36 +0200 Subject: [PATCH 7/7] Div implementations used checked_from_ratio now for more precise panics --- packages/std/src/math/decimal.rs | 14 +++++++++++--- packages/std/src/math/decimal256.rs | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index 220ada17ec..f426030711 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -441,7 +441,15 @@ impl Div for Decimal { type Output = Self; fn div(self, other: Self) -> Self { - Decimal::from_ratio(self.numerator(), other.numerator()) + match Decimal::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } } } forward_ref_binop!(impl Div, div for Decimal, Decimal); @@ -1348,13 +1356,13 @@ mod tests { } #[test] - #[should_panic(expected = "Multiplication overflow")] + #[should_panic(expected = "Division failed - multiplication overflow")] fn decimal_div_overflow_panics() { let _value = Decimal::MAX / Decimal::percent(10); } #[test] - #[should_panic(expected = "Denominator must not be zero")] + #[should_panic(expected = "Division failed - denominator must not be zero")] fn decimal_div_by_zero_panics() { let _value = Decimal::one() / Decimal::zero(); } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index 736f8a5350..67d216b3e7 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -453,7 +453,15 @@ impl Div for Decimal256 { type Output = Self; fn div(self, other: Self) -> Self { - Decimal256::from_ratio(self.numerator(), other.numerator()) + match Decimal256::checked_from_ratio(self.numerator(), other.numerator()) { + Ok(ratio) => ratio, + Err(CheckedFromRatioError::DivideByZero) => { + panic!("Division failed - denominator must not be zero") + } + Err(CheckedFromRatioError::Overflow) => { + panic!("Division failed - multiplication overflow") + } + } } } forward_ref_binop!(impl Div, div for Decimal256, Decimal256); @@ -1439,13 +1447,13 @@ mod tests { } #[test] - #[should_panic(expected = "Multiplication overflow")] + #[should_panic(expected = "Division failed - multiplication overflow")] fn decimal256_div_overflow_panics() { let _value = Decimal256::MAX / Decimal256::percent(10); } #[test] - #[should_panic(expected = "Denominator must not be zero")] + #[should_panic(expected = "Division failed - denominator must not be zero")] fn decimal256_div_by_zero_panics() { let _value = Decimal256::one() / Decimal256::zero(); }