From 8b371ff09a65d9ab77742e3e3aff0a50972efa1b Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 23 Mar 2022 16:20:48 +0100 Subject: [PATCH 1/5] Implement and test `exp` for Ratio Close #290 --- src/si/ratio.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/si/ratio.rs b/src/si/ratio.rs index 47abeba9..e0f9dc16 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -77,6 +77,22 @@ where } } +/// Implementation of various stdlib exponentiation and logarithm functions +#[cfg(feature = "std")] +impl Ratio +where + U: crate::si::Units + ?Sized, + V: crate::num::Float + crate::Conversion, + ratio: crate::Conversion, +{ + /// Returns `e^(self)`, (the exponential function). + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn exp(self) -> Ratio { + Ratio::new::(self.value.exp()) + } +} + mod convert { use super::*; @@ -188,4 +204,21 @@ mod tests { } } } + + #[cfg(feature = "std")] + mod exp_and_log { + storage_types! { + types: Float; + + use crate::si::ratio as r; + use crate::si::quantities::*; + use crate::tests::Test; + + quickcheck! { + fn exp(x: V) -> bool { + Test::eq(&x.exp(), &Ratio::from(x).exp().get::()) + } + } + } + } } From 090fc8de0f442bbf6ec11a2e0cb86cdc1303d81a Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 23 Mar 2022 21:23:17 +0100 Subject: [PATCH 2/5] Merge impl and test mods for inverse-trig and exp --- src/si/ratio.rs | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/si/ratio.rs b/src/si/ratio.rs index e0f9dc16..6cb2c307 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -32,13 +32,14 @@ quantity! { } } -/// Implementation of various stdlib inverse trigonometric functions +/// Implementation of various stdlib functions. #[cfg(feature = "std")] impl Ratio where U: crate::si::Units + ?Sized, V: crate::num::Float + crate::Conversion, radian: crate::Conversion, + ratio: crate::Conversion, { /// Computes the value of the inverse cosine of the ratio. #[inline(always)] @@ -75,16 +76,7 @@ where pub fn atanh(self) -> Angle { Angle::new::(self.value.atanh()) } -} -/// Implementation of various stdlib exponentiation and logarithm functions -#[cfg(feature = "std")] -impl Ratio -where - U: crate::si::Units + ?Sized, - V: crate::num::Float + crate::Conversion, - ratio: crate::Conversion, -{ /// Returns `e^(self)`, (the exponential function). #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] @@ -169,11 +161,12 @@ mod tests { } #[cfg(feature = "std")] - mod inv_trig { + mod float { storage_types! { types: Float; use crate::si::angle as a; + use crate::si::ratio as r; use crate::si::quantities::*; use crate::tests::Test; @@ -201,20 +194,7 @@ mod tests { fn atanh(x: V) -> bool { Test::eq(&x.atanh(), &Ratio::from(x).atanh().get::()) } - } - } - } - - #[cfg(feature = "std")] - mod exp_and_log { - storage_types! { - types: Float; - use crate::si::ratio as r; - use crate::si::quantities::*; - use crate::tests::Test; - - quickcheck! { fn exp(x: V) -> bool { Test::eq(&x.exp(), &Ratio::from(x).exp().get::()) } From 26fbe22665fba7d3644660fe021d6f1dec1b5beb Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 23 Mar 2022 21:45:01 +0100 Subject: [PATCH 3/5] Add zero-arg exponential and log functions --- src/si/ratio.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/si/ratio.rs b/src/si/ratio.rs index 6cb2c307..7dfc23d1 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -83,6 +83,49 @@ where pub fn exp(self) -> Ratio { Ratio::new::(self.value.exp()) } + + /// Returns 2^(self). + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn exp2(self) -> Ratio { + Ratio::new::(self.value.exp2()) + } + + /// Returns the natural logarithm of the number. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn ln(self) -> Ratio { + Ratio::new::(self.value.ln()) + } + + /// Returns the base 2 logarithm of the number. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn log2(self) -> Ratio { + Ratio::new::(self.value.log2()) + } + + /// Returns the base 10 logarithm of the number. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn log10(self) -> Ratio { + Ratio::new::(self.value.log10()) + } + + /// Returns e^(self) - 1 in a way that is accurate even if the number is close to zero. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn exp_m1(self) -> Ratio { + Ratio::new::(self.value.exp_m1()) + } + + /// Returns ln(1+n) (natural logarithm) more accurately than if the + /// operations were performed separately. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn ln_1p(self) -> Ratio { + Ratio::new::(self.value.ln_1p()) + } } mod convert { @@ -198,6 +241,30 @@ mod tests { fn exp(x: V) -> bool { Test::eq(&x.exp(), &Ratio::from(x).exp().get::()) } + + fn exp2(x: V) -> bool { + Test::eq(&x.exp2(), &Ratio::from(x).exp2().get::()) + } + + fn ln(x: V) -> bool { + Test::eq(&x.ln(), &Ratio::from(x).ln().get::()) + } + + fn log2(x: V) -> bool { + Test::eq(&x.log2(), &Ratio::from(x).log2().get::()) + } + + fn log10(x: V) -> bool { + Test::eq(&x.log10(), &Ratio::from(x).log10().get::()) + } + + fn exp_m1(x: V) -> bool { + Test::eq(&x.exp_m1(), &Ratio::from(x).exp_m1().get::()) + } + + fn ln_1p(x: V) -> bool { + Test::eq(&x.ln_1p(), &Ratio::from(x).ln_1p().get::()) + } } } } From 3ff2f8d6133714b7a08265641f53dc41c6cd9577 Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 23 Mar 2022 21:48:53 +0100 Subject: [PATCH 4/5] Add one-arg log function --- src/si/ratio.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/si/ratio.rs b/src/si/ratio.rs index 7dfc23d1..18dccdfa 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -98,6 +98,17 @@ where Ratio::new::(self.value.ln()) } + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation + /// details; self.log2() can produce more accurate results for base 2, and + /// self.log10() can produce more accurate results for base 10. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn log(self, base: V) -> Ratio { + Ratio::new::(self.value.log(base)) + } + /// Returns the base 2 logarithm of the number. #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] @@ -250,6 +261,10 @@ mod tests { Test::eq(&x.ln(), &Ratio::from(x).ln().get::()) } + fn log(x: V, y: V) -> bool { + Test::eq(&x.log(y), &Ratio::from(x).log(y).get::()) + } + fn log2(x: V) -> bool { Test::eq(&x.log2(), &Ratio::from(x).log2().get::()) } From cbdb0015a3054a6a4c2950a99dad7818b5a118a6 Mon Sep 17 00:00:00 2001 From: Jacek Generowicz Date: Wed, 23 Mar 2022 21:50:15 +0100 Subject: [PATCH 5/5] Add must_use attribute to existing inverse-trig fns --- src/si/ratio.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/si/ratio.rs b/src/si/ratio.rs index 18dccdfa..bd789871 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -42,36 +42,42 @@ where ratio: crate::Conversion, { /// Computes the value of the inverse cosine of the ratio. + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] pub fn acos(self) -> Angle { Angle::new::(self.value.acos()) } /// Computes the value of the inverse hyperbolic cosine of the ratio. + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] pub fn acosh(self) -> Angle { Angle::new::(self.value.acosh()) } /// Computes the value of the inverse sine of the ratio. + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] pub fn asin(self) -> Angle { Angle::new::(self.value.asin()) } /// Computes the value of the inverse hyperbolic sine of the ratio. + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] pub fn asinh(self) -> Angle { Angle::new::(self.value.asinh()) } /// Computes the value of the inverse tangent of the ratio. + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] pub fn atan(self) -> Angle { Angle::new::(self.value.atan()) } /// Computes the value of the inverse hyperbolic tangent of the ratio. + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] pub fn atanh(self) -> Angle { Angle::new::(self.value.atanh())