From feb2450e35892162db1c6db8d5a6d3258250c101 Mon Sep 17 00:00:00 2001 From: Mike Boutin Date: Sun, 4 Jun 2017 14:46:11 -0400 Subject: [PATCH] Implement floating point fractional methods. `floor`, `ceil`, `round`, `trunc`, and `fract`. Part of #11 --- CHANGELOG.md | 6 +- src/system.rs | 48 +++++++++++ src/tests.rs | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8660f24..521d6d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,10 @@ ## [Unreleased] ### Added - * [#11](https://github.com/iliekturtles/uom/issues/11) Floating point classification methods - `classify`, `is_finite`, `is_infinte`, `is_nan`, and `is_normal` implemented for `Quantity`. + * [#11](https://github.com/iliekturtles/uom/issues/11) Add floating point classification methods + `classify`, `is_finite`, `is_infinte`, `is_nan`, and `is_normal` for `Quantity`. + * [#11](https://github.com/iliekturtles/uom/issues/11) Add floating point fractional methods + `floor`, `ceil`, `round`, `trunc`, and `fract` for `Quantity`. ## [v0.14.0] — 2017-05-30 diff --git a/src/system.rs b/src/system.rs index 32d0d49e..44535722 100644 --- a/src/system.rs +++ b/src/system.rs @@ -895,6 +895,54 @@ macro_rules! quantity { self.value * >::conversion() / >::conversion() } + + /// Returns the largest integer less than or equal to a number in the given + /// measurement unit. + #[cfg(feature = "std")] + #[inline(always)] + pub fn floor(self, _unit: N) -> Self + where N: Unit<$V>, + { + Self::new::(self.get(_unit).floor()) + } + + /// Returns the smallest integer less than or equal to a number in the given + /// measurement unit. + #[cfg(feature = "std")] + #[inline(always)] + pub fn ceil(self, _unit: N) -> Self + where N: Unit<$V>, + { + Self::new::(self.get(_unit).ceil()) + } + + /// Returns the nearest integer to a number in the in given measurement unit. + /// Round half-way cases away from 0.0. + #[cfg(feature = "std")] + #[inline(always)] + pub fn round(self, _unit: N) -> Self + where N: Unit<$V>, + { + Self::new::(self.get(_unit).round()) + } + + /// Returns the integer part of a number in the given measurement unit. + #[cfg(feature = "std")] + #[inline(always)] + pub fn trunc(self, _unit: N) -> Self + where N: Unit<$V>, + { + Self::new::(self.get(_unit).trunc()) + } + + /// Returns the fractional part of a number in the given measurement unit. + #[cfg(feature = "std")] + #[inline(always)] + pub fn fract(self, _unit: N) -> Self + where N: Unit<$V>, + { + Self::new::(self.get(_unit).fract()) + } } $(impl Unit<$V> for $unit {} diff --git a/src/tests.rs b/src/tests.rs index 972f3f24..fb8054b9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -123,6 +123,116 @@ macro_rules! test { assert_eq!(1.0, m1.get(kilogram)); } + #[cfg(feature = "std")] + #[test] + fn floor() { + let l1 = TLength::new::(3.9999); + let l2 = TLength::new::(3.0001); + let l3 = TLength::new::(3.9999); + let l4 = TLength::new::(3.0001); + let m1 = TMass::new::(3.9999); + let m2 = TMass::new::(3.0001); + + assert_eq!(3.0, l1.floor(kilometer).get(kilometer)); + assert_eq!(3999.0, l1.floor(meter).get(meter)); + assert_eq!(3.0, l2.floor(kilometer).get(kilometer)); + assert_eq!(3000.0, l2.floor(meter).get(meter)); + assert_eq!(0.0, l3.floor(kilometer).get(kilometer)); + assert_eq!(3.0, l3.floor(meter).get(meter)); + assert_eq!(0.0, l4.floor(kilometer).get(kilometer)); + assert_eq!(3.0, l4.floor(meter).get(meter)); + assert_eq!(3.0, m1.floor(kilogram).get(kilogram)); + assert_eq!(3.0, m2.floor(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn ceil() { + let l1 = TLength::new::(3.9999); + let l2 = TLength::new::(3.0001); + let l3 = TLength::new::(3.9999); + let l4 = TLength::new::(3.0001); + let m1 = TMass::new::(3.9999); + let m2 = TMass::new::(3.0001); + + assert_eq!(4.0, l1.ceil(kilometer).get(kilometer)); + assert_eq!(4000.0, l1.ceil(meter).get(meter)); + assert_eq!(4.0, l2.ceil(kilometer).get(kilometer)); + assert_eq!(3001.0, l2.ceil(meter).get(meter)); + assert_eq!(1.0, l3.ceil(kilometer).get(kilometer)); + assert_eq!(4.0, l3.ceil(meter).get(meter)); + assert_eq!(1.0, l4.ceil(kilometer).get(kilometer)); + assert_eq!(4.0, l4.ceil(meter).get(meter)); + assert_eq!(4.0, m1.ceil(kilogram).get(kilogram)); + assert_eq!(4.0, m2.ceil(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn round() { + let l1 = TLength::new::(3.3); + let l2 = TLength::new::(3.5); + let l3 = TLength::new::(3.3); + let l4 = TLength::new::(3.5); + let m1 = TMass::new::(3.3); + let m2 = TMass::new::(3.5); + + assert_eq!(3.0, l1.round(kilometer).get(kilometer)); + assert_eq!(3300.0, l1.round(meter).get(meter)); + assert_eq!(4.0, l2.round(kilometer).get(kilometer)); + assert_eq!(3500.0, l2.round(meter).get(meter)); + assert_eq!(0.0, l3.round(kilometer).get(kilometer)); + assert_eq!(3.0, l3.round(meter).get(meter)); + assert_eq!(0.0, l4.round(kilometer).get(kilometer)); + assert_eq!(4.0, l4.round(meter).get(meter)); + assert_eq!(3.0, m1.round(kilogram).get(kilogram)); + assert_eq!(4.0, m2.round(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn trunc() { + let l1 = TLength::new::(3.3); + let l2 = TLength::new::(3.5); + let l3 = TLength::new::(3.3); + let l4 = TLength::new::(3.5); + let m1 = TMass::new::(3.3); + let m2 = TMass::new::(3.5); + + assert_eq!(3.0, l1.trunc(kilometer).get(kilometer)); + assert_eq!(3300.0, l1.trunc(meter).get(meter)); + assert_eq!(3.0, l2.trunc(kilometer).get(kilometer)); + assert_eq!(3500.0, l2.trunc(meter).get(meter)); + assert_eq!(0.0, l3.trunc(kilometer).get(kilometer)); + assert_eq!(3.0, l3.trunc(meter).get(meter)); + assert_eq!(0.0, l4.trunc(kilometer).get(kilometer)); + assert_eq!(3.0, l4.trunc(meter).get(meter)); + assert_eq!(3.0, m1.trunc(kilogram).get(kilogram)); + assert_eq!(3.0, m2.trunc(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn fract() { + let l1 = TLength::new::(3.3); + let l2 = TLength::new::(3.5); + let l3 = TLength::new::(3.3); + let l4 = TLength::new::(3.5); + let m1 = TMass::new::(3.3); + let m2 = TMass::new::(3.5); + + ulps_eq!(0.3, l1.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.0, l1.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.0, l2.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.5, l2.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.0033, l3.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.3, l3.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.00035, l4.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.5, l4.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.3, m1.fract(kilogram).get(kilogram), epsilon = EPSILON); + ulps_eq!(0.5, m2.fract(kilogram).get(kilogram), epsilon = EPSILON); + } + #[test] fn conversion() { assert_eq!(1000.0, kilometer::conversion()); @@ -434,6 +544,116 @@ macro_rules! test { ulps_eq!(1.0, m1.get(kilogram)); } + #[cfg(feature = "std")] + #[test] + fn floor() { + let l1 = k::TLength::new::(3.9999); + let l2 = k::TLength::new::(3.0001); + let l3 = k::TLength::new::(3.9999); + let l4 = k::TLength::new::(3.0001); + let m1 = k::TMass::new::(3.9999); + let m2 = k::TMass::new::(3.0001); + + assert_eq!(3.0, l1.floor(kilometer).get(kilometer)); + assert_eq!(3999.0, l1.floor(meter).get(meter)); + assert_eq!(3.0, l2.floor(kilometer).get(kilometer)); + assert_eq!(3000.0, l2.floor(meter).get(meter)); + assert_eq!(0.0, l3.floor(kilometer).get(kilometer)); + assert_eq!(3.0, l3.floor(meter).get(meter)); + assert_eq!(0.0, l4.floor(kilometer).get(kilometer)); + assert_eq!(3.0, l4.floor(meter).get(meter)); + assert_eq!(3.0, m1.floor(kilogram).get(kilogram)); + assert_eq!(3.0, m2.floor(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn ceil() { + let l1 = k::TLength::new::(3.9999); + let l2 = k::TLength::new::(3.0001); + let l3 = k::TLength::new::(3.9999); + let l4 = k::TLength::new::(3.0001); + let m1 = k::TMass::new::(3.9999); + let m2 = k::TMass::new::(3.0001); + + assert_eq!(4.0, l1.ceil(kilometer).get(kilometer)); + assert_eq!(4000.0, l1.ceil(meter).get(meter)); + assert_eq!(4.0, l2.ceil(kilometer).get(kilometer)); + assert_eq!(3001.0, l2.ceil(meter).get(meter)); + assert_eq!(1.0, l3.ceil(kilometer).get(kilometer)); + assert_eq!(4.0, l3.ceil(meter).get(meter)); + assert_eq!(1.0, l4.ceil(kilometer).get(kilometer)); + assert_eq!(4.0, l4.ceil(meter).get(meter)); + assert_eq!(4.0, m1.ceil(kilogram).get(kilogram)); + assert_eq!(4.0, m2.ceil(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn round() { + let l1 = k::TLength::new::(3.3); + let l2 = k::TLength::new::(3.5); + let l3 = k::TLength::new::(3.3); + let l4 = k::TLength::new::(3.5); + let m1 = k::TMass::new::(3.3); + let m2 = k::TMass::new::(3.5); + + assert_eq!(3.0, l1.round(kilometer).get(kilometer)); + assert_eq!(3300.0, l1.round(meter).get(meter)); + assert_eq!(4.0, l2.round(kilometer).get(kilometer)); + assert_eq!(3500.0, l2.round(meter).get(meter)); + assert_eq!(0.0, l3.round(kilometer).get(kilometer)); + assert_eq!(3.0, l3.round(meter).get(meter)); + assert_eq!(0.0, l4.round(kilometer).get(kilometer)); + assert_eq!(4.0, l4.round(meter).get(meter)); + assert_eq!(3.0, m1.round(kilogram).get(kilogram)); + assert_eq!(4.0, m2.round(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn trunc() { + let l1 = k::TLength::new::(3.3); + let l2 = k::TLength::new::(3.5); + let l3 = k::TLength::new::(3.3); + let l4 = k::TLength::new::(3.5); + let m1 = k::TMass::new::(3.3); + let m2 = k::TMass::new::(3.5); + + assert_eq!(3.0, l1.trunc(kilometer).get(kilometer)); + assert_eq!(3300.0, l1.trunc(meter).get(meter)); + assert_eq!(3.0, l2.trunc(kilometer).get(kilometer)); + assert_eq!(3500.0, l2.trunc(meter).get(meter)); + assert_eq!(0.0, l3.trunc(kilometer).get(kilometer)); + assert_eq!(3.0, l3.trunc(meter).get(meter)); + assert_eq!(0.0, l4.trunc(kilometer).get(kilometer)); + assert_eq!(3.0, l4.trunc(meter).get(meter)); + assert_eq!(3.0, m1.trunc(kilogram).get(kilogram)); + assert_eq!(3.0, m2.trunc(kilogram).get(kilogram)); + } + + #[cfg(feature = "std")] + #[test] + fn fract() { + let l1 = k::TLength::new::(3.3); + let l2 = k::TLength::new::(3.5); + let l3 = k::TLength::new::(3.3); + let l4 = k::TLength::new::(3.5); + let m1 = k::TMass::new::(3.3); + let m2 = k::TMass::new::(3.5); + + ulps_eq!(0.3, l1.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.0, l1.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.0, l2.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.5, l2.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.0033, l3.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.3, l3.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.00035, l4.fract(kilometer).get(kilometer), epsilon = EPSILON); + ulps_eq!(0.5, l4.fract(meter).get(meter), epsilon = EPSILON); + ulps_eq!(0.3, m1.fract(kilogram).get(kilogram), epsilon = EPSILON); + ulps_eq!(0.5, m2.fract(kilogram).get(kilogram), epsilon = EPSILON); + } + quickcheck! { #[allow(trivial_casts)] fn add(l: $V, r: $V) -> bool {