Skip to content

Commit 08efa35

Browse files
authored
Merge pull request #1341 from CosmWasm/1186-checked-methods-for-decimal
`Decimal`/`Decimal256` - implement missing checked methods
2 parents 5ea5912 + 681043a commit 08efa35

File tree

3 files changed

+195
-4
lines changed

3 files changed

+195
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to
1313
`Decimal{,256}::abs_diff` ([#1334]).
1414
- cosmwasm-std: Implement `From<Decimal> for Decimal256`.
1515
- cosmwasm-std: Implement `Rem`/`RemAssign` for `Decimal`/`Decimal256`.
16+
- cosmwasm-std: Implement `checked_add`/`_sub`/`_div`/`_rem` for
17+
`Decimal`/`Decimal256`.
1618

1719
[#1334]: https://github.com/CosmWasm/cosmwasm/pull/1334
1820

packages/std/src/math/decimal.rs

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, S
77
use std::str::FromStr;
88
use thiserror::Error;
99

10-
use crate::errors::{CheckedFromRatioError, CheckedMultiplyRatioError, StdError};
11-
use crate::OverflowError;
10+
use crate::errors::{
11+
CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError,
12+
OverflowOperation, StdError,
13+
};
1214

1315
use super::Fraction;
1416
use super::Isqrt;
@@ -182,6 +184,20 @@ impl Decimal {
182184
Self::DECIMAL_PLACES as u32
183185
}
184186

187+
pub fn checked_add(self, other: Self) -> Result<Self, OverflowError> {
188+
self.0
189+
.checked_add(other.0)
190+
.map(Self)
191+
.map_err(|_| OverflowError::new(OverflowOperation::Add, self, other))
192+
}
193+
194+
pub fn checked_sub(self, other: Self) -> Result<Self, OverflowError> {
195+
self.0
196+
.checked_sub(other.0)
197+
.map(Self)
198+
.map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other))
199+
}
200+
185201
/// Multiplies one `Decimal` by another, returning an `OverflowError` if an overflow occurred.
186202
pub fn checked_mul(self, other: Self) -> Result<Self, OverflowError> {
187203
let result_as_uint256 = self.numerator().full_mul(other.numerator())
@@ -229,6 +245,17 @@ impl Decimal {
229245
})
230246
}
231247

248+
pub fn checked_div(self, other: Self) -> Result<Self, CheckedFromRatioError> {
249+
Decimal::checked_from_ratio(self.numerator(), other.numerator())
250+
}
251+
252+
pub fn checked_rem(self, other: Self) -> Result<Self, DivideByZeroError> {
253+
self.0
254+
.checked_rem(other.0)
255+
.map(Self)
256+
.map_err(|_| DivideByZeroError::new(self))
257+
}
258+
232259
/// Returns the approximate square root as a Decimal.
233260
///
234261
/// This should not overflow or panic.
@@ -1741,4 +1768,71 @@ mod tests {
17411768
fn decimal_rem_panics_for_zero() {
17421769
let _ = Decimal::percent(777) % Decimal::zero();
17431770
}
1771+
1772+
#[test]
1773+
fn decimal_checked_methods() {
1774+
// checked add
1775+
assert_eq!(
1776+
Decimal::percent(402)
1777+
.checked_add(Decimal::percent(111))
1778+
.unwrap(),
1779+
Decimal::percent(513)
1780+
);
1781+
assert!(matches!(
1782+
Decimal::MAX.checked_add(Decimal::percent(1)),
1783+
Err(OverflowError { .. })
1784+
));
1785+
1786+
// checked sub
1787+
assert_eq!(
1788+
Decimal::percent(1111)
1789+
.checked_sub(Decimal::percent(111))
1790+
.unwrap(),
1791+
Decimal::percent(1000)
1792+
);
1793+
assert!(matches!(
1794+
Decimal::zero().checked_sub(Decimal::percent(1)),
1795+
Err(OverflowError { .. })
1796+
));
1797+
1798+
// checked div
1799+
assert_eq!(
1800+
Decimal::percent(30)
1801+
.checked_div(Decimal::percent(200))
1802+
.unwrap(),
1803+
Decimal::percent(15)
1804+
);
1805+
assert_eq!(
1806+
Decimal::percent(88)
1807+
.checked_div(Decimal::percent(20))
1808+
.unwrap(),
1809+
Decimal::percent(440)
1810+
);
1811+
assert!(matches!(
1812+
Decimal::MAX.checked_div(Decimal::zero()),
1813+
Err(CheckedFromRatioError::DivideByZero { .. })
1814+
));
1815+
assert!(matches!(
1816+
Decimal::MAX.checked_div(Decimal::percent(1)),
1817+
Err(CheckedFromRatioError::Overflow { .. })
1818+
));
1819+
1820+
// checked rem
1821+
assert_eq!(
1822+
Decimal::percent(402)
1823+
.checked_rem(Decimal::percent(111))
1824+
.unwrap(),
1825+
Decimal::percent(69)
1826+
);
1827+
assert_eq!(
1828+
Decimal::percent(1525)
1829+
.checked_rem(Decimal::percent(400))
1830+
.unwrap(),
1831+
Decimal::percent(325)
1832+
);
1833+
assert!(matches!(
1834+
Decimal::MAX.checked_rem(Decimal::zero()),
1835+
Err(DivideByZeroError { .. })
1836+
));
1837+
}
17441838
}

packages/std/src/math/decimal256.rs

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, S
77
use std::str::FromStr;
88
use thiserror::Error;
99

10-
use crate::errors::{CheckedFromRatioError, CheckedMultiplyRatioError, StdError};
11-
use crate::{Decimal, OverflowError, Uint512};
10+
use crate::errors::{
11+
CheckedFromRatioError, CheckedMultiplyRatioError, DivideByZeroError, OverflowError,
12+
OverflowOperation, StdError,
13+
};
14+
use crate::{Decimal, Uint512};
1215

1316
use super::Fraction;
1417
use super::Isqrt;
@@ -194,6 +197,20 @@ impl Decimal256 {
194197
Self::DECIMAL_PLACES as u32
195198
}
196199

200+
pub fn checked_add(self, other: Self) -> Result<Self, OverflowError> {
201+
self.0
202+
.checked_add(other.0)
203+
.map(Self)
204+
.map_err(|_| OverflowError::new(OverflowOperation::Add, self, other))
205+
}
206+
207+
pub fn checked_sub(self, other: Self) -> Result<Self, OverflowError> {
208+
self.0
209+
.checked_sub(other.0)
210+
.map(Self)
211+
.map_err(|_| OverflowError::new(OverflowOperation::Sub, self, other))
212+
}
213+
197214
/// Multiplies one `Decimal256` by another, returning an `OverflowError` if an overflow occurred.
198215
pub fn checked_mul(self, other: Self) -> Result<Self, OverflowError> {
199216
let result_as_uint512 = self.numerator().full_mul(other.numerator())
@@ -241,6 +258,17 @@ impl Decimal256 {
241258
})
242259
}
243260

261+
pub fn checked_div(self, other: Self) -> Result<Self, CheckedFromRatioError> {
262+
Decimal256::checked_from_ratio(self.numerator(), other.numerator())
263+
}
264+
265+
pub fn checked_rem(self, other: Self) -> Result<Self, DivideByZeroError> {
266+
self.0
267+
.checked_rem(other.0)
268+
.map(Self)
269+
.map_err(|_| DivideByZeroError::new(self))
270+
}
271+
244272
/// Returns the approximate square root as a Decimal256.
245273
///
246274
/// This should not overflow or panic.
@@ -1887,4 +1915,71 @@ mod tests {
18871915
fn decimal256_rem_panics_for_zero() {
18881916
let _ = Decimal256::percent(777) % Decimal256::zero();
18891917
}
1918+
1919+
#[test]
1920+
fn decimal256_checked_methods() {
1921+
// checked add
1922+
assert_eq!(
1923+
Decimal256::percent(402)
1924+
.checked_add(Decimal256::percent(111))
1925+
.unwrap(),
1926+
Decimal256::percent(513)
1927+
);
1928+
assert!(matches!(
1929+
Decimal256::MAX.checked_add(Decimal256::percent(1)),
1930+
Err(OverflowError { .. })
1931+
));
1932+
1933+
// checked sub
1934+
assert_eq!(
1935+
Decimal256::percent(1111)
1936+
.checked_sub(Decimal256::percent(111))
1937+
.unwrap(),
1938+
Decimal256::percent(1000)
1939+
);
1940+
assert!(matches!(
1941+
Decimal256::zero().checked_sub(Decimal256::percent(1)),
1942+
Err(OverflowError { .. })
1943+
));
1944+
1945+
// checked div
1946+
assert_eq!(
1947+
Decimal256::percent(30)
1948+
.checked_div(Decimal256::percent(200))
1949+
.unwrap(),
1950+
Decimal256::percent(15)
1951+
);
1952+
assert_eq!(
1953+
Decimal256::percent(88)
1954+
.checked_div(Decimal256::percent(20))
1955+
.unwrap(),
1956+
Decimal256::percent(440)
1957+
);
1958+
assert!(matches!(
1959+
Decimal256::MAX.checked_div(Decimal256::zero()),
1960+
Err(CheckedFromRatioError::DivideByZero { .. })
1961+
));
1962+
assert!(matches!(
1963+
Decimal256::MAX.checked_div(Decimal256::percent(1)),
1964+
Err(CheckedFromRatioError::Overflow { .. })
1965+
));
1966+
1967+
// checked rem
1968+
assert_eq!(
1969+
Decimal256::percent(402)
1970+
.checked_rem(Decimal256::percent(111))
1971+
.unwrap(),
1972+
Decimal256::percent(69)
1973+
);
1974+
assert_eq!(
1975+
Decimal256::percent(1525)
1976+
.checked_rem(Decimal256::percent(400))
1977+
.unwrap(),
1978+
Decimal256::percent(325)
1979+
);
1980+
assert!(matches!(
1981+
Decimal256::MAX.checked_rem(Decimal256::zero()),
1982+
Err(DivideByZeroError { .. })
1983+
));
1984+
}
18901985
}

0 commit comments

Comments
 (0)