From 15617c79a4871454c058f5782da6cf89fa9f9e86 Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 8 Jul 2022 13:43:24 +0200 Subject: [PATCH 1/3] Decimal: Implement missing checked methods --- packages/std/src/math/decimal.rs | 87 +++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index ed3284b09b..e91c43809e 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -7,8 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, S use std::str::FromStr; use thiserror::Error; -use crate::errors::{CheckedFromRatioError, CheckedMultiplyRatioError, StdError}; -use crate::OverflowError; +use crate::errors::{CheckedFromRatioError, CheckedMultiplyRatioError, StdError, OverflowError, OverflowOperation, DivideByZeroError}; use super::Fraction; use super::Isqrt; @@ -182,6 +181,33 @@ impl Decimal { Self::DECIMAL_PLACES as u32 } + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_div(self, other: Self) -> Result { + Decimal::checked_from_ratio(self.numerator(), other.numerator()) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + + + /// Multiplies one `Decimal` by another, returning an `OverflowError` if an overflow occurred. pub fn checked_mul(self, other: Self) -> Result { let result_as_uint256 = self.numerator().full_mul(other.numerator()) @@ -1741,4 +1767,61 @@ mod tests { fn decimal_rem_panics_for_zero() { let _ = Decimal::percent(777) % Decimal::zero(); } + + #[test] + fn decimal_checked_methods() { + // checked add + assert_eq!( + Decimal::percent(402).checked_add(Decimal::percent(111)).unwrap(), + Decimal::percent(513) + ); + assert!(matches!( + Decimal::MAX.checked_add(Decimal::percent(1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + Decimal::percent(1111).checked_sub(Decimal::percent(111)).unwrap(), + Decimal::percent(1000) + ); + assert!(matches!( + Decimal::zero().checked_sub(Decimal::percent(1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + Decimal::percent(30).checked_div(Decimal::percent(200)).unwrap(), + Decimal::percent(15) + ); + assert_eq!( + Decimal::percent(88).checked_div(Decimal::percent(20)).unwrap(), + Decimal::percent(440) + ); + assert!(matches!( + Decimal::MAX.checked_div(Decimal::zero()), + Err(CheckedFromRatioError::DivideByZero { .. }) + )); + assert!(matches!( + Decimal::MAX.checked_div(Decimal::percent(1)), + Err(CheckedFromRatioError::Overflow { .. }) + )); + + // checked rem + assert_eq!( + Decimal::percent(402).checked_rem(Decimal::percent(111)).unwrap(), + Decimal::percent(69) + ); + assert_eq!( + Decimal::percent(1525).checked_rem(Decimal::percent(400)).unwrap(), + Decimal::percent(325) + ); + assert!(matches!( + Decimal::MAX.checked_rem(Decimal::zero()), + Err(DivideByZeroError { .. }) + )); + } + + } From a459a1b9aaefd4e4a0ba1c850b06a88a38b8f306 Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 8 Jul 2022 15:10:31 +0200 Subject: [PATCH 2/3] Decimal256: Implement missing checked methods --- packages/std/src/math/decimal.rs | 55 +++++++++------- packages/std/src/math/decimal256.rs | 99 ++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 24 deletions(-) diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs index e91c43809e..13bc9b8082 100644 --- a/packages/std/src/math/decimal.rs +++ b/packages/std/src/math/decimal.rs @@ -7,7 +7,10 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, S use std::str::FromStr; use thiserror::Error; -use crate::errors::{CheckedFromRatioError, CheckedMultiplyRatioError, StdError, OverflowError, OverflowOperation, DivideByZeroError}; +use crate::errors::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, StdError, +}; use super::Fraction; use super::Isqrt; @@ -195,19 +198,6 @@ impl Decimal { .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) } - pub fn checked_div(self, other: Self) -> Result { - Decimal::checked_from_ratio(self.numerator(), other.numerator()) - } - - pub fn checked_rem(self, other: Self) -> Result { - self.0 - .checked_rem(other.0) - .map(Self) - .map_err(|_| DivideByZeroError::new(self)) - } - - - /// Multiplies one `Decimal` by another, returning an `OverflowError` if an overflow occurred. pub fn checked_mul(self, other: Self) -> Result { let result_as_uint256 = self.numerator().full_mul(other.numerator()) @@ -255,6 +245,17 @@ impl Decimal { }) } + pub fn checked_div(self, other: Self) -> Result { + Decimal::checked_from_ratio(self.numerator(), other.numerator()) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + /// Returns the approximate square root as a Decimal. /// /// This should not overflow or panic. @@ -1772,7 +1773,9 @@ mod tests { fn decimal_checked_methods() { // checked add assert_eq!( - Decimal::percent(402).checked_add(Decimal::percent(111)).unwrap(), + Decimal::percent(402) + .checked_add(Decimal::percent(111)) + .unwrap(), Decimal::percent(513) ); assert!(matches!( @@ -1782,7 +1785,9 @@ mod tests { // checked sub assert_eq!( - Decimal::percent(1111).checked_sub(Decimal::percent(111)).unwrap(), + Decimal::percent(1111) + .checked_sub(Decimal::percent(111)) + .unwrap(), Decimal::percent(1000) ); assert!(matches!( @@ -1792,11 +1797,15 @@ mod tests { // checked div assert_eq!( - Decimal::percent(30).checked_div(Decimal::percent(200)).unwrap(), + Decimal::percent(30) + .checked_div(Decimal::percent(200)) + .unwrap(), Decimal::percent(15) ); assert_eq!( - Decimal::percent(88).checked_div(Decimal::percent(20)).unwrap(), + Decimal::percent(88) + .checked_div(Decimal::percent(20)) + .unwrap(), Decimal::percent(440) ); assert!(matches!( @@ -1810,11 +1819,15 @@ mod tests { // checked rem assert_eq!( - Decimal::percent(402).checked_rem(Decimal::percent(111)).unwrap(), + Decimal::percent(402) + .checked_rem(Decimal::percent(111)) + .unwrap(), Decimal::percent(69) ); assert_eq!( - Decimal::percent(1525).checked_rem(Decimal::percent(400)).unwrap(), + Decimal::percent(1525) + .checked_rem(Decimal::percent(400)) + .unwrap(), Decimal::percent(325) ); assert!(matches!( @@ -1822,6 +1835,4 @@ mod tests { Err(DivideByZeroError { .. }) )); } - - } diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index e98ed8fea5..c0f9005f8b 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -7,8 +7,11 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, S use std::str::FromStr; use thiserror::Error; -use crate::errors::{CheckedFromRatioError, CheckedMultiplyRatioError, StdError}; -use crate::{Decimal, OverflowError, Uint512}; +use crate::errors::{ + CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError, + OverflowOperation, StdError, +}; +use crate::{Decimal, Uint512}; use super::Fraction; use super::Isqrt; @@ -194,6 +197,20 @@ impl Decimal256 { Self::DECIMAL_PLACES as u32 } + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other)) + } + /// Multiplies one `Decimal256` by another, returning an `OverflowError` if an overflow occurred. pub fn checked_mul(self, other: Self) -> Result { let result_as_uint512 = self.numerator().full_mul(other.numerator()) @@ -241,6 +258,17 @@ impl Decimal256 { }) } + pub fn checked_div(self, other: Self) -> Result { + Decimal256::checked_from_ratio(self.numerator(), other.numerator()) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .map_err(|_| DivideByZeroError::new(self)) + } + /// Returns the approximate square root as a Decimal256. /// /// This should not overflow or panic. @@ -1887,4 +1915,71 @@ mod tests { fn decimal256_rem_panics_for_zero() { let _ = Decimal256::percent(777) % Decimal256::zero(); } + + #[test] + fn decimal256_checked_methods() { + // checked add + assert_eq!( + Decimal256::percent(402) + .checked_add(Decimal256::percent(111)) + .unwrap(), + Decimal256::percent(513) + ); + assert!(matches!( + Decimal256::MAX.checked_add(Decimal256::percent(1)), + Err(OverflowError { .. }) + )); + + // checked sub + assert_eq!( + Decimal256::percent(1111) + .checked_sub(Decimal256::percent(111)) + .unwrap(), + Decimal256::percent(1000) + ); + assert!(matches!( + Decimal256::zero().checked_sub(Decimal256::percent(1)), + Err(OverflowError { .. }) + )); + + // checked div + assert_eq!( + Decimal256::percent(30) + .checked_div(Decimal256::percent(200)) + .unwrap(), + Decimal256::percent(15) + ); + assert_eq!( + Decimal256::percent(88) + .checked_div(Decimal256::percent(20)) + .unwrap(), + Decimal256::percent(440) + ); + assert!(matches!( + Decimal256::MAX.checked_div(Decimal256::zero()), + Err(CheckedFromRatioError::DivideByZero { .. }) + )); + assert!(matches!( + Decimal256::MAX.checked_div(Decimal256::percent(1)), + Err(CheckedFromRatioError::Overflow { .. }) + )); + + // checked rem + assert_eq!( + Decimal256::percent(402) + .checked_rem(Decimal256::percent(111)) + .unwrap(), + Decimal256::percent(69) + ); + assert_eq!( + Decimal256::percent(1525) + .checked_rem(Decimal256::percent(400)) + .unwrap(), + Decimal256::percent(325) + ); + assert!(matches!( + Decimal256::MAX.checked_rem(Decimal256::zero()), + Err(DivideByZeroError { .. }) + )); + } } From 681043a994a6b4132e76fe6071a9f020ce25ff05 Mon Sep 17 00:00:00 2001 From: Jakub Bogucki Date: Fri, 8 Jul 2022 15:12:02 +0200 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a41b566e61..44baac9c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to `Decimal{,256}::abs_diff` ([#1334]). - cosmwasm-std: Implement `From for Decimal256`. - cosmwasm-std: Implement `Rem`/`RemAssign` for `Decimal`/`Decimal256`. +- cosmwasm-std: Implement `checked_add`/`_sub`/`_div`/`_rem` for + `Decimal`/`Decimal256`. [#1334]: https://github.com/CosmWasm/cosmwasm/pull/1334