From 33bf9f9df234974819ef508c2f79fc12894e0169 Mon Sep 17 00:00:00 2001 From: Erin Power Date: Wed, 17 Mar 2021 08:17:38 +0100 Subject: [PATCH] Add copysign (upstream PR rust-num#207) Signed-off-by: Fredrick Brennan --- Cargo.toml | 1 + build.rs | 17 ++++++++--- src/float.rs | 86 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 48feb2b..68392bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ std = [] # vestigial features, now always in effect i128 = [] +copysign = [] [build-dependencies] autocfg = "1" diff --git a/build.rs b/build.rs index 639d058..683e299 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,20 @@ +extern crate autocfg; + use std::env; fn main() { let ac = autocfg::new(); + // If the "i128" feature is explicity requested, don't bother probing for it. + // It will still cause a build error if that was set improperly. + if env::var_os("CARGO_FEATURE_I128").is_some() || ac.probe_type("i128") { + autocfg::emit("has_i128"); + } + + if env::var_os("CARGO_FEATURE_COPYSIGN").is_some() || ac.probe_expression("f32::copysign") { + autocfg::emit("has_copysign"); + } + ac.emit_expression_cfg( "unsafe { 1f64.to_int_unchecked::() }", "has_to_int_unchecked", @@ -10,11 +22,6 @@ fn main() { ac.emit_expression_cfg("1u32.reverse_bits()", "has_reverse_bits"); ac.emit_expression_cfg("1u32.trailing_ones()", "has_leading_trailing_ones"); - ac.emit_expression_cfg("1u32.div_euclid(1u32)", "has_div_euclid"); - - if env::var_os("CARGO_FEATURE_STD").is_some() { - ac.emit_expression_cfg("1f64.copysign(-1f64)", "has_copysign"); - } autocfg::rerun_path("build.rs"); } diff --git a/src/float.rs b/src/float.rs index 33cbf9c..632e3e5 100644 --- a/src/float.rs +++ b/src/float.rs @@ -1,10 +1,14 @@ +use core::mem; use core::num::FpCategory; use core::ops::{Add, Div, Neg}; use core::f32; use core::f64; -use crate::{Num, NumCast, ToPrimitive}; +use {Num, NumCast, ToPrimitive}; + +#[cfg(all(not(feature = "std"), feature = "libm"))] +use libm; /// Generic trait for floating point numbers that works with `no_std`. /// @@ -166,7 +170,6 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// check(0.0f64, false); /// ``` #[inline] - #[allow(clippy::eq_op)] fn is_nan(self) -> bool { self != self } @@ -367,10 +370,12 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } else { self - f + one } - } else if -f < h { - self - f } else { - self - f - one + if -f < h { + self - f + } else { + self - f - one + } } } @@ -503,7 +508,8 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns `true` if `self` is positive, including `+0.0` and - /// `FloatCore::infinity()`, and `FloatCore::nan()`. + /// `FloatCore::infinity()`, and since Rust 1.20 also + /// `FloatCore::nan()`. /// /// # Examples /// @@ -521,7 +527,6 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// check(-0.0f64, false); /// check(f64::NEG_INFINITY, false); /// check(f64::MIN_POSITIVE, true); - /// check(f64::NAN, true); /// check(-f64::NAN, false); /// ``` #[inline] @@ -530,7 +535,8 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns `true` if `self` is negative, including `-0.0` and - /// `FloatCore::neg_infinity()`, and `-FloatCore::nan()`. + /// `FloatCore::neg_infinity()`, and since Rust 1.20 also + /// `-FloatCore::nan()`. /// /// # Examples /// @@ -549,7 +555,6 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// check(f64::NEG_INFINITY, true); /// check(f64::MIN_POSITIVE, false); /// check(f64::NAN, false); - /// check(-f64::NAN, true); /// ``` #[inline] fn is_sign_negative(self) -> bool { @@ -764,7 +769,9 @@ impl FloatCore for f32 { const EXP_MASK: u32 = 0x7f800000; const MAN_MASK: u32 = 0x007fffff; - let bits: u32 = self.to_bits(); + // Safety: this identical to the implementation of f32::to_bits(), + // which is only available starting at Rust 1.20 + let bits: u32 = unsafe { mem::transmute(self) }; match (bits & MAN_MASK, bits & EXP_MASK) { (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, @@ -779,7 +786,10 @@ impl FloatCore for f32 { fn is_sign_negative(self) -> bool { const SIGN_MASK: u32 = 0x80000000; - self.to_bits() & SIGN_MASK != 0 + // Safety: this identical to the implementation of f32::to_bits(), + // which is only available starting at Rust 1.20 + let bits: u32 = unsafe { mem::transmute(self) }; + bits & SIGN_MASK != 0 } #[inline] @@ -861,7 +871,9 @@ impl FloatCore for f64 { const EXP_MASK: u64 = 0x7ff0000000000000; const MAN_MASK: u64 = 0x000fffffffffffff; - let bits: u64 = self.to_bits(); + // Safety: this identical to the implementation of f64::to_bits(), + // which is only available starting at Rust 1.20 + let bits: u64 = unsafe { mem::transmute(self) }; match (bits & MAN_MASK, bits & EXP_MASK) { (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, @@ -876,7 +888,10 @@ impl FloatCore for f64 { fn is_sign_negative(self) -> bool { const SIGN_MASK: u64 = 0x8000000000000000; - self.to_bits() & SIGN_MASK != 0 + // Safety: this identical to the implementation of f64::to_bits(), + // which is only available starting at Rust 1.20 + let bits: u64 = unsafe { mem::transmute(self) }; + bits & SIGN_MASK != 0 } #[inline] @@ -1251,13 +1266,12 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { fn signum(self) -> Self; /// Returns `true` if `self` is positive, including `+0.0`, - /// `Float::infinity()`, and `Float::nan()`. + /// `Float::infinity()`, and since Rust 1.20 also `Float::nan()`. /// /// ``` /// use num_traits::Float; /// use std::f64; /// - /// let nan: f64 = f64::NAN; /// let neg_nan: f64 = -f64::NAN; /// /// let f = 7.0; @@ -1265,20 +1279,18 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { /// /// assert!(f.is_sign_positive()); /// assert!(!g.is_sign_positive()); - /// assert!(nan.is_sign_positive()); /// assert!(!neg_nan.is_sign_positive()); /// ``` fn is_sign_positive(self) -> bool; /// Returns `true` if `self` is negative, including `-0.0`, - /// `Float::neg_infinity()`, and `-Float::nan()`. + /// `Float::neg_infinity()`, and since Rust 1.20 also `-Float::nan()`. /// /// ``` /// use num_traits::Float; /// use std::f64; /// /// let nan: f64 = f64::NAN; - /// let neg_nan: f64 = -f64::NAN; /// /// let f = 7.0; /// let g = -7.0; @@ -1286,7 +1298,6 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { /// assert!(!f.is_sign_negative()); /// assert!(g.is_sign_negative()); /// assert!(!nan.is_sign_negative()); - /// assert!(neg_nan.is_sign_negative()); /// ``` fn is_sign_negative(self) -> bool; @@ -2014,7 +2025,9 @@ macro_rules! float_impl_libm { } fn integer_decode_f32(f: f32) -> (u64, i16, i8) { - let bits: u32 = f.to_bits(); + // Safety: this identical to the implementation of f32::to_bits(), + // which is only available starting at Rust 1.20 + let bits: u32 = unsafe { mem::transmute(f) }; let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 }; let mut exponent: i16 = ((bits >> 23) & 0xff) as i16; let mantissa = if exponent == 0 { @@ -2028,7 +2041,9 @@ fn integer_decode_f32(f: f32) -> (u64, i16, i8) { } fn integer_decode_f64(f: f64) -> (u64, i16, i8) { - let bits: u64 = f.to_bits(); + // Safety: this identical to the implementation of f64::to_bits(), + // which is only available starting at Rust 1.20 + let bits: u64 = unsafe { mem::transmute(f) }; let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16; let mantissa = if exponent == 0 { @@ -2229,7 +2244,7 @@ mod tests { #[test] fn convert_deg_rad() { - use crate::float::FloatCore; + use float::FloatCore; for &(deg, rad) in &DEG_RAD_PAIRS { assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-6); @@ -2245,7 +2260,7 @@ mod tests { #[test] fn convert_deg_rad_std() { for &(deg, rad) in &DEG_RAD_PAIRS { - use crate::Float; + use Float; assert!((Float::to_degrees(rad) - deg).abs() < 1e-6); assert!((Float::to_radians(deg) - rad).abs() < 1e-6); @@ -2257,8 +2272,11 @@ mod tests { } #[test] + // This fails with the forwarded `std` implementation in Rust 1.8. + // To avoid the failure, the test is limited to `no_std` builds. + #[cfg(not(feature = "std"))] fn to_degrees_rounding() { - use crate::float::FloatCore; + use float::FloatCore; assert_eq!( FloatCore::to_degrees(1_f32), @@ -2269,7 +2287,7 @@ mod tests { #[test] #[cfg(any(feature = "std", feature = "libm"))] fn extra_logs() { - use crate::float::{Float, FloatConst}; + use float::{Float, FloatConst}; fn check(diff: F) { let _2 = F::from(2.0).unwrap(); @@ -2288,7 +2306,7 @@ mod tests { #[test] #[cfg(any(feature = "std", feature = "libm"))] fn copysign() { - use crate::float::Float; + 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()); test_copysignf(2.0_f32, -2.0_f32, f32::nan()); @@ -2296,25 +2314,25 @@ mod tests { #[cfg(any(feature = "std", feature = "libm"))] fn test_copysignf(p: f32, n: f32, nan: f32) { - use crate::float::Float; + use float::Float; use core::ops::Neg; assert!(p.is_sign_positive()); assert!(n.is_sign_negative()); assert!(nan.is_nan()); - assert_eq!(p, Float::copysign(p, p)); - assert_eq!(p.neg(), Float::copysign(p, n)); + assert_eq!(p, p.copysign(p)); + assert_eq!(p.neg(), p.copysign(n)); - assert_eq!(n, Float::copysign(n, n)); - assert_eq!(n.neg(), Float::copysign(n, p)); + assert_eq!(n, n.copysign(n)); + assert_eq!(n.neg(), n.copysign(p)); - assert!(Float::copysign(nan, p).is_sign_positive()); - assert!(Float::copysign(nan, n).is_sign_negative()); + assert!(nan.copysign(p).is_sign_positive()); + assert!(nan.copysign(n).is_sign_negative()); } #[cfg(any(feature = "std", feature = "libm"))] - fn test_copysign_generic(p: F, n: F, nan: F) { + fn test_copysign_generic(p: F, n: F, nan: F) { assert!(p.is_sign_positive()); assert!(n.is_sign_negative()); assert!(nan.is_nan());