Skip to content

Commit

Permalink
fixes to fractions
Browse files Browse the repository at this point in the history
- temperatures in bundled units have fractions disabled
- fix error rounding up/down in Number::new_approx (there were 2 errors
  one making that the other would be harder to catch :_) )
- added new tests to avoid all of this in the future
  • Loading branch information
Zheoni committed Nov 28, 2023
1 parent c193761 commit 2960014
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 18 deletions.
38 changes: 20 additions & 18 deletions src/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,11 @@ impl Number {
/// - It can't be represented with the given restrictions as a fraction.
/// - The number is not positive.
///
/// It will return `Number::Regular` when the number is an integer.
/// It will return `Number::Regular` when the number is an integer with less
/// than a 1e-10 margin of error.
///
/// Otherwise it will return a `Number::Fraction`. `num` can be 0 if the
/// value is rounded to an integer.
///
/// `accuracy` is a value between 0 and 1 representing the error percent.
///
Expand Down Expand Up @@ -868,27 +872,18 @@ impl Number {
return None;
}

if decimal == 0.0 {
if decimal < 1e-10 {
return Some(Self::Regular(value));
}

// Round down
if decimal < max_err && whole > 0 {
let rounded = value.round() as u32;
let round_err = value - value.round();
if round_err.abs() < max_err && rounded > 0 && rounded <= max_whole {
return Some(Self::Fraction {
whole,
whole: rounded,
num: 0,
den: 1,
err: decimal,
});
}

// Round up
if (1.0 - decimal < max_err) && whole < max_whole {
return Some(Self::Fraction {
whole: whole + 1,
num: 0,
den: 1,
err: 1.0 - decimal,
err: round_err,
});
}

Expand Down Expand Up @@ -940,13 +935,20 @@ mod tests {
};
}

#[test_case(1.0 => matches Some(Number::Regular(v)) if v == 1.0 ; "no exact")]
#[test_case(1.0 => matches Some(Number::Regular(v)) if v == 1.0 ; "exact")]
#[test_case(1.00000000001 => matches Some(Number::Regular(v)) if 1.0 - v < 1e-10 && v > 1.0 ; "exactish")]
#[test_case(0.01 => None ; "no approx 0")]
#[test_case(1.9999 => matches frac!(2) ; "round up")]
#[test_case(1.0001 => matches frac!(1) ; "round down")]
#[test_case(400.0001 => matches frac!(400) ; "not wrong round up")]
#[test_case(399.9999 => matches frac!(400) ; "not wrong round down")]
#[test_case(1.5 => matches frac!(1, 1, 2) ; "trivial frac")]
#[test_case(0.2501 => matches frac!(1, 4) ; "frac with err")]
fn fractions(value: f64) -> Option<Number> {
Number::new_approx(value, 0.05, 4, u32::MAX)
let num = Number::new_approx(value, 0.05, 4, u32::MAX);
if let Some(num) = num {
assert!((num.value() - value).abs() < 10e-9);
}
num
}
}
3 changes: 3 additions & 0 deletions tests/fractions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ use test_case::test_case;
#[test_case(3.5, "tsp" => "3 1/2 tsp")]
#[test_case(15.0, "tsp" => "5 tbsp")]
#[test_case(16.0, "tsp" => "1/3 c")]
#[test_case(180.0, "C" => "356 °F")]
#[test_case(499.999, "lb" => "500 lb")]
#[test_case(1.5, "F" => "1.5 °F")]
fn imperial(value: f64, unit: &str) -> String {
let converter = Converter::default();
let mut q = Quantity::new(Value::from(value), Some(unit.to_string()));
Expand Down
2 changes: 2 additions & 0 deletions units.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ imperial = true
tsp = { max_whole = 5, max_denominator = 2 }
tbsp = { max_whole = 4, max_denominator = 3 }
lb = { max_denominator = 8 }
F = false
C = false

[[quantity]]
quantity = "volume"
Expand Down

0 comments on commit 2960014

Please sign in to comment.