Skip to content

Commit

Permalink
Decimal/Decimal256: Implement checked_from_ratio
Browse files Browse the repository at this point in the history
  • Loading branch information
ueco-jb committed Apr 26, 2022
1 parent f442125 commit dd9e5b5
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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`

### Changed

Expand Down
37 changes: 32 additions & 5 deletions packages/std/src/math/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
use std::str::FromStr;
use thiserror::Error;

use crate::errors::StdError;
use crate::errors::{CheckedMultiplyRatioError, StdError};
use crate::OverflowError;

use super::Fraction;
Expand Down Expand Up @@ -118,16 +118,30 @@ impl Decimal {

/// Returns the ratio (numerator / denominator) as a Decimal
pub fn from_ratio(numerator: impl Into<Uint128>, denominator: impl Into<Uint128>) -> Self {
match Decimal::checked_from_ratio(numerator, denominator) {
Ok(value) => value,
Err(CheckedMultiplyRatioError::DivideByZero) => {
panic!("Denominator must not be zero")
}
Err(CheckedMultiplyRatioError::Overflow) => panic!("Multiplication overflow"),
}
}

/// Returns the ratio (numerator / denominator) as a Decimal
pub fn checked_from_ratio(
numerator: impl Into<Uint128>,
denominator: impl Into<Uint128>,
) -> Result<Self, CheckedMultiplyRatioError> {
let numerator: Uint128 = numerator.into();
let denominator: Uint128 = denominator.into();
if denominator.is_zero() {
panic!("Denominator must not be zero");
return Err(CheckedMultiplyRatioError::DivideByZero);
}

Decimal(
Ok(Decimal(
// numerator * DECIMAL_FRACTIONAL / denominator
numerator.multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator),
)
numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator)?,
))
}

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

#[test]
fn decimal_checked_from_ratio_does_not_panic() {
assert_eq!(
Decimal::checked_from_ratio(1u128, 0u128),
Err(CheckedMultiplyRatioError::DivideByZero)
);

assert_eq!(
Decimal::checked_from_ratio(u128::MAX, 1u128),
Err(CheckedMultiplyRatioError::Overflow)
);
}

#[test]
fn decimal_implements_fraction() {
let fraction = Decimal::from_str("1234.567").unwrap();
Expand Down
37 changes: 32 additions & 5 deletions packages/std/src/math/decimal256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
use std::str::FromStr;
use thiserror::Error;

use crate::errors::StdError;
use crate::errors::{CheckedMultiplyRatioError, StdError};
use crate::{OverflowError, Uint512};

use super::Fraction;
Expand Down Expand Up @@ -130,16 +130,30 @@ impl Decimal256 {

/// Returns the ratio (numerator / denominator) as a Decimal256
pub fn from_ratio(numerator: impl Into<Uint256>, denominator: impl Into<Uint256>) -> Self {
match Decimal256::checked_from_ratio(numerator, denominator) {
Ok(value) => value,
Err(CheckedMultiplyRatioError::DivideByZero) => {
panic!("Denominator must not be zero")
}
Err(CheckedMultiplyRatioError::Overflow) => panic!("Multiplication overflow"),
}
}

/// Returns the ratio (numerator / denominator) as a Decimal256
pub fn checked_from_ratio(
numerator: impl Into<Uint256>,
denominator: impl Into<Uint256>,
) -> Result<Self, CheckedMultiplyRatioError> {
let numerator: Uint256 = numerator.into();
let denominator: Uint256 = denominator.into();
if denominator.is_zero() {
panic!("Denominator must not be zero");
return Err(CheckedMultiplyRatioError::DivideByZero);
}

Self(
Ok(Self(
// numerator * DECIMAL_FRACTIONAL / denominator
numerator.multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator),
)
numerator.checked_multiply_ratio(Self::DECIMAL_FRACTIONAL, denominator)?,
))
}

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

#[test]
fn decimal256_checked_from_ratio_does_not_panic() {
assert_eq!(
Decimal256::checked_from_ratio(1u128, 0u128),
Err(CheckedMultiplyRatioError::DivideByZero)
);

assert_eq!(
Decimal256::checked_from_ratio(u128::MAX, 1u128),
Err(CheckedMultiplyRatioError::Overflow)
);
}

#[test]
fn decimal256_implements_fraction() {
let fraction = Decimal256::from_str("1234.567").unwrap();
Expand Down

0 comments on commit dd9e5b5

Please sign in to comment.