Skip to content

Commit

Permalink
fix: add missing mathematical operations to Numeric (#4976)
Browse files Browse the repository at this point in the history
* fix: add missing mathematical operations to `Numeric`
* fix: make operations on `Numeric` spec aware

---------

Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
mversic authored Aug 21, 2024
1 parent 2810e1b commit 6c8cbf2
Showing 1 changed file with 68 additions and 8 deletions.
76 changes: 68 additions & 8 deletions primitives/numeric/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,26 +175,75 @@ impl Numeric {
self.inner.scale()
}

/// Checked addition
///
/// # Errors
/// In case of overflow
/// Checked addition. Computes `self + other`, returning `None` if overflow occurred
pub fn checked_add(self, other: Self) -> Option<Self> {
self.inner
.checked_add(other.inner)
.map(|inner| Self { inner })
}

/// Checked subtraction
///
/// # Errors
/// In case of overflow
/// Checked subtraction. Computes `self - other`, returning `None` if overflow occurred
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.inner
.checked_sub(other.inner)
.and_then(|inner| inner.is_sign_positive().then_some(Self { inner }))
}

/// Checked multiplication. Computes `self * other`, returning `None` if overflow occurred
pub fn checked_mul(self, other: Self, spec: NumericSpec) -> Option<Self> {
self.inner
.checked_mul(other.inner)
.map(|inner| {
if let Some(scale) = spec.scale {
return inner.round_dp(scale);
}

inner
})
.map(|inner| Self { inner })
}

/// Checked division. Computes `self / other`, returning `None` if overflow occurred.
pub fn checked_div(self, other: Self, spec: NumericSpec) -> Option<Self> {
self.inner
.checked_div(other.inner)
.map(|inner| {
if let Some(scale) = spec.scale {
return inner.round_dp(scale);
}

inner
})
.map(|inner| Self { inner })
}

/// Checked remainder. Computes `self % other`, returning `None` if overflow occurred.
pub fn checked_rem(self, other: Self, spec: NumericSpec) -> Option<Self> {
self.inner
.checked_rem(other.inner)
.map(|inner| {
if let Some(scale) = spec.scale {
return inner.round_dp(scale);
}

inner
})
.map(|inner| Self { inner })
}

/// Returns a new `Decimal` number rounded to the given spec.
/// Rounding follows “Bankers Rounding” rules. e.g. 6.5 -> 6, 7.5 -> 8
#[must_use]
pub fn round(&self, spec: NumericSpec) -> Self {
if let Some(scale) = spec.scale {
return Self {
inner: self.inner.round_dp(scale),
};
}

Self { inner: self.inner }
}

/// Convert [`Numeric`] to [`f64`] with possible loss in precision
pub fn to_f64(self) -> f64 {
self.inner.to_f64().expect("never fails")
Expand All @@ -218,6 +267,17 @@ impl From<u64> for Numeric {
}
}

impl TryFrom<f64> for Numeric {
type Error = TryFromNumericError;

fn try_from(value: f64) -> Result<Self, Self::Error> {
value
.try_into()
.map_err(|_| TryFromNumericError)
.map(|inner| Self { inner })
}
}

impl TryFrom<Numeric> for u32 {
type Error = TryFromNumericError;

Expand Down

0 comments on commit 6c8cbf2

Please sign in to comment.