diff --git a/src/lib.rs b/src/lib.rs index b1cf2a4..de6d6b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,6 +257,46 @@ impl Ratio { } } + /// Rounds to the nearest integer. Rounds half-way cases away to even. + pub fn round_ties_even(&self) -> Ratio { + let zero: Ratio = Zero::zero(); + let one: T = One::one(); + let two: T = one.clone() + one.clone(); + + // Find unsigned fractional part of rational number + let mut fractional = self.fract(); + if fractional < zero { + fractional = zero - fractional + }; + + // Compare the unsigned fractional part with 1/2 + let half = Ratio::new_raw(one, two); + match fractional.cmp(&half) { + cmp::Ordering::Greater => { + let one: Ratio = One::one(); + if *self >= Zero::zero() { + self.trunc() + one + } else { + self.trunc() - one + } + } + cmp::Ordering::Equal => { + let trunc = self.trunc(); + if trunc.numer().is_even() { + trunc + } else { + let one: Ratio = One::one(); + if *self >= Zero::zero() { + self.trunc() + one + } else { + self.trunc() - one + } + } + } + cmp::Ordering::Less => self.trunc(), + } + } + /// Rounds towards zero. #[inline] pub fn trunc(&self) -> Ratio { @@ -1742,6 +1782,7 @@ mod test { pub const _0: Rational64 = Ratio { numer: 0, denom: 1 }; pub const _1: Rational64 = Ratio { numer: 1, denom: 1 }; pub const _2: Rational64 = Ratio { numer: 2, denom: 1 }; + pub const _3: Rational64 = Ratio { numer: 3, denom: 1 }; pub const _NEG2: Rational64 = Ratio { numer: -2, denom: 1, @@ -2650,38 +2691,51 @@ mod test { assert_eq!(_1_3.ceil(), _1); assert_eq!(_1_3.floor(), _0); assert_eq!(_1_3.round(), _0); + assert_eq!(_1_3.round_ties_even(), _0); assert_eq!(_1_3.trunc(), _0); assert_eq!(_NEG1_3.ceil(), _0); assert_eq!(_NEG1_3.floor(), -_1); assert_eq!(_NEG1_3.round(), _0); + assert_eq!(_NEG1_3.round_ties_even(), _0); assert_eq!(_NEG1_3.trunc(), _0); assert_eq!(_2_3.ceil(), _1); assert_eq!(_2_3.floor(), _0); assert_eq!(_2_3.round(), _1); + assert_eq!(_2_3.round_ties_even(), _1); assert_eq!(_2_3.trunc(), _0); assert_eq!(_NEG2_3.ceil(), _0); assert_eq!(_NEG2_3.floor(), -_1); assert_eq!(_NEG2_3.round(), -_1); + assert_eq!(_NEG2_3.round_ties_even(), -_1); assert_eq!(_NEG2_3.trunc(), _0); assert_eq!(_1_2.ceil(), _1); assert_eq!(_1_2.floor(), _0); assert_eq!(_1_2.round(), _1); + assert_eq!(_1_2.round_ties_even(), _0); assert_eq!(_1_2.trunc(), _0); assert_eq!(_NEG1_2.ceil(), _0); assert_eq!(_NEG1_2.floor(), -_1); assert_eq!(_NEG1_2.round(), -_1); + assert_eq!(_NEG1_2.round_ties_even(), _0); assert_eq!(_NEG1_2.trunc(), _0); assert_eq!(_1.ceil(), _1); assert_eq!(_1.floor(), _1); assert_eq!(_1.round(), _1); + assert_eq!(_1.round_ties_even(), _1); assert_eq!(_1.trunc(), _1); + assert_eq!(_5_2.ceil(), _3); + assert_eq!(_5_2.floor(), _2); + assert_eq!(_5_2.round(), _3); + assert_eq!(_5_2.round_ties_even(), _2); + assert_eq!(_5_2.trunc(), _2); + // Overflow checks let _neg1 = Ratio::from_integer(-1); @@ -2702,6 +2756,14 @@ mod test { assert_eq!(_large_rat6.round(), _neg1); assert_eq!(_large_rat7.round(), Zero::zero()); assert_eq!(_large_rat8.round(), Zero::zero()); + assert_eq!(_large_rat1.round_ties_even(), One::one()); + assert_eq!(_large_rat2.round_ties_even(), One::one()); + assert_eq!(_large_rat3.round_ties_even(), One::one()); + assert_eq!(_large_rat4.round_ties_even(), One::one()); + assert_eq!(_large_rat5.round_ties_even(), _neg1); + assert_eq!(_large_rat6.round_ties_even(), _neg1); + assert_eq!(_large_rat7.round_ties_even(), Zero::zero()); + assert_eq!(_large_rat8.round_ties_even(), Zero::zero()); } #[test]