From 6320d849eee45f5d21d7aa5c429907a76ed2242d Mon Sep 17 00:00:00 2001 From: Erin Power Date: Wed, 17 Mar 2021 08:17:38 +0100 Subject: [PATCH] Add copysign --- Cargo.toml | 1 + build.rs | 4 ++++ src/float.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 8d88bf44..813d36d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ libm = { version = "0.2.0", optional = true } default = ["std"] std = [] i128 = [] +copysign = [] [build-dependencies] autocfg = "1" diff --git a/build.rs b/build.rs index 816ddadd..4f5147d7 100644 --- a/build.rs +++ b/build.rs @@ -11,6 +11,10 @@ fn main() { autocfg::emit("has_i128"); } + if env::var_os("CARGO_FEATURE_COPYSIGN").is_some() || ac.probe_path("f32::copysign") { + autocfg::emit("has_copysign"); + } + ac.emit_expression_cfg( "unsafe { 1f64.to_int_unchecked::() }", "has_to_int_unchecked", diff --git a/src/float.rs b/src/float.rs index ce1b4acf..b40a55ac 100644 --- a/src/float.rs +++ b/src/float.rs @@ -1840,6 +1840,35 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { /// assert!(abs_difference < 1e-10); /// ``` fn integer_decode(self) -> (u64, i16, i8); + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a `NAN`, then a `NAN` with the sign of + /// `sign` is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 3.5_f32; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f32); + /// assert_eq!(f.copysign(-0.42), -3.5_f32); + /// assert_eq!((-f).copysign(0.42), 3.5_f32); + /// assert_eq!((-f).copysign(-0.42), -3.5_f32); + /// + /// assert!(f32::nan().copysign(1.0).is_nan()); + /// ``` + fn copysign(self, sign: Self) -> Self { + if self.is_sign_negative() == sign.is_sign_negative() { + self + } else { + self.neg() + } + } } #[cfg(feature = "std")] @@ -1917,6 +1946,12 @@ macro_rules! float_impl_std { Self::acosh(self) -> Self; Self::atanh(self) -> Self; } + + #[cfg(has_copysign)] + #[inline] + fn copysign(self, sign: Self) -> Self { + Self::copysign(self, sign) + } } }; } @@ -2096,6 +2131,11 @@ impl Float for f64 { libm::fmax as max(self, other: Self) -> Self; libm::fmin as min(self, other: Self) -> Self; } + + #[cfg(has_copysign)] + forward! { + libm::copysign as copysign(self, sign: Self) -> Self; + } } macro_rules! float_const_impl { @@ -2243,4 +2283,28 @@ mod tests { check::(1e-6); check::(1e-12); } + + #[cfg(all(any(feature = "std", feature = "libm"), has_copysign))] + #[test] + fn copysign() { + use float::Float; + test_copysign_generic(2.0_f32, -2.0_f32, f32::nan()); + test_copysign_generic(2.0_f64, -2.0_f64, f64::nan()); + } + + #[cfg(all(any(feature = "std", feature = "libm"), has_copysign))] + fn test_copysign_generic(p: F, n: F, nan: F) { + assert!(p.is_sign_positive()); + assert!(n.is_sign_negative()); + assert!(nan.is_nan()); + + assert_eq!(p, p.copysign(p)); + assert_eq!(p.neg(), p.copysign(n)); + + assert_eq!(n, n.copysign(n)); + assert_eq!(n.neg(), n.copysign(p)); + + assert!(nan.copysign(p).is_sign_positive()); + assert!(nan.copysign(n).is_sign_negative()); + } }