Skip to content

Commit

Permalink
Add copysign (upstream PR rust-num#207)
Browse files Browse the repository at this point in the history
Signed-off-by: Fredrick Brennan <copypaste@kittens.ph>
  • Loading branch information
XAMPPRocky authored and ctrlcctrlv committed Mar 28, 2023
1 parent b5906ee commit 60fd4ea
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -27,6 +27,7 @@ std = []

# vestigial features, now always in effect
i128 = []
copysign = []

[build-dependencies]
autocfg = "1"
17 changes: 12 additions & 5 deletions build.rs
@@ -1,20 +1,27 @@
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::<i32>() }",
"has_to_int_unchecked",
);

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");
}
86 changes: 52 additions & 34 deletions 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`.
///
Expand Down Expand Up @@ -166,7 +170,6 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
/// check(0.0f64, false);
/// ```
#[inline]
#[allow(clippy::eq_op)]
fn is_nan(self) -> bool {
self != self
}
Expand Down Expand Up @@ -367,10 +370,12 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + 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
}
}
}

Expand Down Expand Up @@ -503,7 +508,8 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + 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
///
Expand All @@ -521,7 +527,6 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + 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]
Expand All @@ -530,7 +535,8 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + 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
///
Expand All @@ -549,7 +555,6 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + 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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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]
Expand Down Expand Up @@ -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,
Expand All @@ -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]
Expand Down Expand Up @@ -1251,42 +1266,38 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
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;
/// let g = -7.0;
///
/// 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;
///
/// 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;

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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),
Expand All @@ -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<F: Float + FloatConst>(diff: F) {
let _2 = F::from(2.0).unwrap();
Expand All @@ -2288,33 +2306,33 @@ 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());
}

#[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<F: crate::float::Float + ::core::fmt::Debug>(p: F, n: F, nan: F) {
fn test_copysign_generic<F: ::float::Float + core::fmt::Debug>(p: F, n: F, nan: F) {
assert!(p.is_sign_positive());
assert!(n.is_sign_negative());
assert!(nan.is_nan());
Expand Down

0 comments on commit 60fd4ea

Please sign in to comment.