Skip to content

Commit e604447

Browse files
committed
Decimal/Decimal256: Implement checked_from_ratio
1 parent f442125 commit e604447

File tree

3 files changed

+65
-10
lines changed

3 files changed

+65
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to
1010

1111
- cosmwasm-std: Implement `checked_multiply_ratio` for
1212
`Uint64`/`Uint128`/`Uint256`
13+
- cosmwasm-std: Implement `checked_from_ratio` for `Decimal`/`Decimal256`
1314

1415
### Changed
1516

packages/std/src/math/decimal.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
77
use std::str::FromStr;
88
use thiserror::Error;
99

10-
use crate::errors::StdError;
10+
use crate::errors::{CheckedMultiplyRatioError, StdError};
1111
use crate::OverflowError;
1212

1313
use super::Fraction;
@@ -118,16 +118,30 @@ impl Decimal {
118118

119119
/// Returns the ratio (numerator / denominator) as a Decimal
120120
pub fn from_ratio(numerator: impl Into<Uint128>, denominator: impl Into<Uint128>) -> Self {
121+
match Decimal::checked_from_ratio(numerator, denominator) {
122+
Ok(value) => value,
123+
Err(CheckedMultiplyRatioError::DivideByZero) => {
124+
panic!("Denominator must not be zero")
125+
}
126+
Err(CheckedMultiplyRatioError::Overflow) => panic!("Multiplication overflow"),
127+
}
128+
}
129+
130+
/// Returns the ratio (numerator / denominator) as a Decimal
131+
pub fn checked_from_ratio(
132+
numerator: impl Into<Uint128>,
133+
denominator: impl Into<Uint128>,
134+
) -> Result<Self, CheckedMultiplyRatioError> {
121135
let numerator: Uint128 = numerator.into();
122136
let denominator: Uint128 = denominator.into();
123137
if denominator.is_zero() {
124-
panic!("Denominator must not be zero");
138+
return Err(CheckedMultiplyRatioError::DivideByZero);
125139
}
126140

127-
Decimal(
141+
Ok(Decimal(
128142
// numerator * DECIMAL_FRACTIONAL / denominator
129-
numerator.multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator),
130-
)
143+
numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator)?,
144+
))
131145
}
132146

133147
pub const fn is_zero(&self) -> bool {
@@ -658,6 +672,19 @@ mod tests {
658672
Decimal::from_ratio(1u128, 0u128);
659673
}
660674

675+
#[test]
676+
fn decimal_checked_from_ratio_does_not_panic() {
677+
assert_eq!(
678+
Decimal::checked_from_ratio(1u128, 0u128),
679+
Err(CheckedMultiplyRatioError::DivideByZero)
680+
);
681+
682+
assert_eq!(
683+
Decimal::checked_from_ratio(u128::MAX, 1u128),
684+
Err(CheckedMultiplyRatioError::Overflow)
685+
);
686+
}
687+
661688
#[test]
662689
fn decimal_implements_fraction() {
663690
let fraction = Decimal::from_str("1234.567").unwrap();

packages/std/src/math/decimal256.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
77
use std::str::FromStr;
88
use thiserror::Error;
99

10-
use crate::errors::StdError;
10+
use crate::errors::{CheckedMultiplyRatioError, StdError};
1111
use crate::{OverflowError, Uint512};
1212

1313
use super::Fraction;
@@ -130,16 +130,30 @@ impl Decimal256 {
130130

131131
/// Returns the ratio (numerator / denominator) as a Decimal256
132132
pub fn from_ratio(numerator: impl Into<Uint256>, denominator: impl Into<Uint256>) -> Self {
133+
match Decimal256::checked_from_ratio(numerator, denominator) {
134+
Ok(value) => value,
135+
Err(CheckedMultiplyRatioError::DivideByZero) => {
136+
panic!("Denominator must not be zero")
137+
}
138+
Err(CheckedMultiplyRatioError::Overflow) => panic!("Multiplication overflow"),
139+
}
140+
}
141+
142+
/// Returns the ratio (numerator / denominator) as a Decimal256
143+
pub fn checked_from_ratio(
144+
numerator: impl Into<Uint256>,
145+
denominator: impl Into<Uint256>,
146+
) -> Result<Self, CheckedMultiplyRatioError> {
133147
let numerator: Uint256 = numerator.into();
134148
let denominator: Uint256 = denominator.into();
135149
if denominator.is_zero() {
136-
panic!("Denominator must not be zero");
150+
return Err(CheckedMultiplyRatioError::DivideByZero);
137151
}
138152

139-
Self(
153+
Ok(Self(
140154
// numerator * DECIMAL_FRACTIONAL / denominator
141-
numerator.multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator),
142-
)
155+
numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator)?,
156+
))
143157
}
144158

145159
pub const fn is_zero(&self) -> bool {
@@ -690,6 +704,19 @@ mod tests {
690704
Decimal256::from_ratio(1u128, 0u128);
691705
}
692706

707+
#[test]
708+
fn decimal256_checked_from_ratio_does_not_panic() {
709+
assert_eq!(
710+
Decimal256::checked_from_ratio(1u128, 0u128),
711+
Err(CheckedMultiplyRatioError::DivideByZero)
712+
);
713+
714+
assert_eq!(
715+
Decimal256::checked_from_ratio(Uint256::MAX, 1u128),
716+
Err(CheckedMultiplyRatioError::Overflow)
717+
);
718+
}
719+
693720
#[test]
694721
fn decimal256_implements_fraction() {
695722
let fraction = Decimal256::from_str("1234.567").unwrap();

0 commit comments

Comments
 (0)