Skip to content

Commit

Permalink
feat: replace from_scaled with try_from_scaled
Browse files Browse the repository at this point in the history
  • Loading branch information
OliverNChalk committed Sep 19, 2024
1 parent 08f7e6e commit abcf55d
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 33 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to `const-decimal`.

## Unreleased

## 0.3.0

- BREAKING: Remove `from_scaled` in favor of `try_from_scaled`.

## 0.2.2

- Correctly format `Decimal::ZERO` as `0.0...` not `-0.0...`.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "const-decimal"
description = "Integer-backed decimals with constant precision"
version = "0.2.2"
version = "0.3.0"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["Oliver Chalk"]
Expand Down
37 changes: 12 additions & 25 deletions src/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,18 @@ where
Decimal(I::max_value())
}

// TODO: Add `try_from_scaled() -> Result<>`.

/// Losslessly converts a scaled integer to this type.
///
/// # Panics
///
/// Panics if the integer cannot be represented without precision
/// loss/overflow.
///
/// # Examples
///
/// ```rust
/// use const_decimal::Decimal;
///
/// let five = Decimal::<u64, 3>::from_scaled(5, 0);
/// let five = Decimal::<u64, 3>::try_from_scaled(5, 0).unwrap();
/// assert_eq!(five, Decimal::TWO + Decimal::TWO + Decimal::ONE);
/// assert_eq!(five.0, 5000);
/// ```
pub fn from_scaled(integer: I, scale: u8) -> Self {
pub fn try_from_scaled(integer: I, scale: u8) -> Option<Self> {
match scale.cmp(&D) {
Ordering::Greater => {
// SAFETY: We know `scale > D` so this cannot underflow.
Expand All @@ -59,25 +52,21 @@ where
// SAFETY: `divisor` cannot be zero as `x.pow(y)` cannot return 0.
#[allow(clippy::arithmetic_side_effects)]
let remainder = integer % divisor;
assert!(
remainder == I::ZERO,
"Cast would lose precision; input={integer}; scale={scale}; divisor={divisor}"
);
if remainder != I::ZERO {
// NB: Cast would lose precision.
return None;
}

// SAFETY: `Decimal::mul` panics on divide by zero.
#[allow(clippy::arithmetic_side_effects)]
Decimal(integer / divisor)
integer.checked_div(&divisor).map(Decimal)
}
Ordering::Less => {
// SAFETY: We know `scale < D` so this cannot underflow.
#[allow(clippy::arithmetic_side_effects)]
let multiplier = I::TEN.pow(u32::from(D - scale));

// SAFETY: `Decimal::mul` panics on overflow.
#[allow(clippy::arithmetic_side_effects)]
Decimal(integer * multiplier)
integer.checked_mul(&multiplier).map(Decimal)
}
Ordering::Equal => Decimal(integer),
Ordering::Equal => Some(Decimal(integer)),
}
}

Expand Down Expand Up @@ -649,14 +638,12 @@ mod tests {
let decimals = u8::try_from(decimals_percent * max_decimals / 100).unwrap();
let scaling = I::TEN.pow(decimals as u32);

let out = std::panic::catch_unwind(|| {
Decimal::from_scaled(integer, decimals)
});
let out = Decimal::try_from_scaled(integer, decimals);
let reference_out = Rational::from_integers(integer.into(), scaling.into());

match out {
Ok(out) => assert_eq!(Rational::from(out), reference_out),
Err(_) => {
Some(out) => assert_eq!(Rational::from(out), reference_out),
None => {
let scaling: Integer = Decimal::<I, D>::SCALING_FACTOR.into();
let remainder = &scaling % Integer::from(reference_out.denominator_ref());
let information = &reference_out * Rational::from(scaling);
Expand Down
21 changes: 15 additions & 6 deletions src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@ mod tests {
#[test]
fn uint64_9_to_string() {
assert_eq!(Uint64_9::ONE.to_string(), "1.000000000");
assert_eq!(Uint64_9::from_scaled(123, 9).to_string(), "0.000000123");
assert_eq!((Uint64_9::ONE + Uint64_9::from_scaled(123, 9)).to_string(), "1.000000123");
assert_eq!(Uint64_9::try_from_scaled(123, 9).unwrap().to_string(), "0.000000123");
assert_eq!(
(Uint64_9::ONE + Uint64_9::try_from_scaled(123, 9).unwrap()).to_string(),
"1.000000123"
);
}

#[test]
Expand Down Expand Up @@ -152,11 +155,17 @@ mod tests {
fn int64_9_to_string() {
assert_eq!(Int64_9::ZERO.to_string(), "0.000000000");
assert_eq!(Int64_9::ONE.to_string(), "1.000000000");
assert_eq!(Int64_9::from_scaled(123, 9).to_string(), "0.000000123");
assert_eq!((Int64_9::ONE + Int64_9::from_scaled(123, 9)).to_string(), "1.000000123");
assert_eq!(Int64_9::try_from_scaled(123, 9).unwrap().to_string(), "0.000000123");
assert_eq!(
(Int64_9::ONE + Int64_9::try_from_scaled(123, 9).unwrap()).to_string(),
"1.000000123"
);
assert_eq!((-Int64_9::ONE).to_string(), "-1.000000000");
assert_eq!((-Int64_9::from_scaled(123, 9)).to_string(), "-0.000000123");
assert_eq!((-Int64_9::ONE + -Int64_9::from_scaled(123, 9)).to_string(), "-1.000000123");
assert_eq!((-Int64_9::try_from_scaled(123, 9).unwrap()).to_string(), "-0.000000123");
assert_eq!(
(-Int64_9::ONE + -Int64_9::try_from_scaled(123, 9).unwrap()).to_string(),
"-1.000000123"
);
}

#[test]
Expand Down

0 comments on commit abcf55d

Please sign in to comment.