Implement IEEE 754-2019 minimun and maximum functions for f32/f64

This commit is contained in:
Loïc BRANSTETT 2021-11-18 11:48:52 +01:00
parent 41301c3b23
commit a8ee0e9c2c
6 changed files with 199 additions and 0 deletions

View File

@ -703,6 +703,68 @@ impl f32 {
intrinsics::minnumf32(self, other)
}
/// Returns the maximum of the two numbers, propagating NaNs.
///
/// This returns NaN when *either* argument is NaN, as opposed to
/// [`f32::max`] which only returns NaN when *both* arguments are NaN.
///
/// ```
/// #![feature(float_minimum_maximum)]
/// let x = 1.0f32;
/// let y = 2.0f32;
///
/// assert_eq!(x.maximum(y), y);
/// assert!(x.maximum(f32::NAN).is_nan());
/// ```
///
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
/// Note that this follow the semantics specified in IEEE 754-2019.
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
#[inline]
pub fn maximum(self, other: f32) -> f32 {
if self > other {
self
} else if other > self {
other
} else if self == other {
if self.is_sign_positive() && other.is_sign_negative() { self } else { other }
} else {
self + other
}
}
/// Returns the minimum of the two numbers, propagating NaNs.
///
/// This returns NaN when *either* argument is NaN, as opposed to
/// [`f32::min`] which only returns NaN when *both* arguments are NaN.
///
/// ```
/// #![feature(float_minimum_maximum)]
/// let x = 1.0f32;
/// let y = 2.0f32;
///
/// assert_eq!(x.minimum(y), x);
/// assert!(x.minimum(f32::NAN).is_nan());
/// ```
///
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
/// Note that this follow the semantics specified in IEEE 754-2019.
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
#[inline]
pub fn minimum(self, other: f32) -> f32 {
if self < other {
self
} else if other < self {
other
} else if self == other {
if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
} else {
self + other
}
}
/// Rounds toward zero and converts to any primitive integer type,
/// assuming that the value is finite and fits in that type.
///

View File

@ -719,6 +719,68 @@ impl f64 {
intrinsics::minnumf64(self, other)
}
/// Returns the maximum of the two numbers, propagating NaNs.
///
/// This returns NaN when *either* argument is NaN, as opposed to
/// [`f64::max`] which only returns NaN when *both* arguments are NaN.
///
/// ```
/// #![feature(float_minimum_maximum)]
/// let x = 1.0_f64;
/// let y = 2.0_f64;
///
/// assert_eq!(x.maximum(y), y);
/// assert!(x.maximum(f64::NAN).is_nan());
/// ```
///
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
/// Note that this follow the semantics specified in IEEE 754-2019.
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
#[inline]
pub fn maximum(self, other: f64) -> f64 {
if self > other {
self
} else if other > self {
other
} else if self == other {
if self.is_sign_positive() && other.is_sign_negative() { self } else { other }
} else {
self + other
}
}
/// Returns the minimum of the two numbers, propagating NaNs.
///
/// This returns NaN when *either* argument is NaN, as opposed to
/// [`f64::min`] which only returns NaN when *both* arguments are NaN.
///
/// ```
/// #![feature(float_minimum_maximum)]
/// let x = 1.0_f64;
/// let y = 2.0_f64;
///
/// assert_eq!(x.minimum(y), x);
/// assert!(x.minimum(f64::NAN).is_nan());
/// ```
///
/// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser
/// of the two numbers. For this operation, -0.0 is considered to be less than +0.0.
/// Note that this follow the semantics specified in IEEE 754-2019.
#[unstable(feature = "float_minimum_maximum", issue = "91079")]
#[inline]
pub fn minimum(self, other: f64) -> f64 {
if self < other {
self
} else if other < self {
other
} else if self == other {
if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
} else {
self + other
}
}
/// Rounds toward zero and converts to any primitive integer type,
/// assuming that the value is finite and fits in that type.
///

View File

@ -27,6 +27,7 @@
#![feature(extern_types)]
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(float_minimum_maximum)]
#![feature(array_from_fn)]
#![feature(hashmap_internals)]
#![feature(try_find)]

View File

@ -715,6 +715,67 @@ macro_rules! test_float {
assert!(($nan as $fty).max($nan).is_nan());
}
#[test]
fn minimum() {
assert_eq!((0.0 as $fty).minimum(0.0), 0.0);
assert!((0.0 as $fty).minimum(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).minimum(0.0), -0.0);
assert!((-0.0 as $fty).minimum(0.0).is_sign_negative());
assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0);
assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative());
assert_eq!((9.0 as $fty).minimum(9.0), 9.0);
assert_eq!((-9.0 as $fty).minimum(0.0), -9.0);
assert_eq!((0.0 as $fty).minimum(9.0), 0.0);
assert!((0.0 as $fty).minimum(9.0).is_sign_positive());
assert_eq!((-0.0 as $fty).minimum(9.0), -0.0);
assert!((-0.0 as $fty).minimum(9.0).is_sign_negative());
assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0);
assert_eq!(($inf as $fty).minimum(9.0), 9.0);
assert_eq!((9.0 as $fty).minimum($inf), 9.0);
assert_eq!(($inf as $fty).minimum(-9.0), -9.0);
assert_eq!((-9.0 as $fty).minimum($inf), -9.0);
assert_eq!(($neginf as $fty).minimum(9.0), $neginf);
assert_eq!((9.0 as $fty).minimum($neginf), $neginf);
assert_eq!(($neginf as $fty).minimum(-9.0), $neginf);
assert_eq!((-9.0 as $fty).minimum($neginf), $neginf);
assert!(($nan as $fty).minimum(9.0).is_nan());
assert!(($nan as $fty).minimum(-9.0).is_nan());
assert!((9.0 as $fty).minimum($nan).is_nan());
assert!((-9.0 as $fty).minimum($nan).is_nan());
assert!(($nan as $fty).minimum($nan).is_nan());
}
#[test]
fn maximum() {
assert_eq!((0.0 as $fty).maximum(0.0), 0.0);
assert!((0.0 as $fty).maximum(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).maximum(0.0), 0.0);
assert!((-0.0 as $fty).maximum(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0);
assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative());
assert_eq!((9.0 as $fty).maximum(9.0), 9.0);
assert_eq!((-9.0 as $fty).maximum(0.0), 0.0);
assert!((-9.0 as $fty).maximum(0.0).is_sign_positive());
assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0);
assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative());
assert_eq!((0.0 as $fty).maximum(9.0), 9.0);
assert_eq!((0.0 as $fty).maximum(-9.0), 0.0);
assert!((0.0 as $fty).maximum(-9.0).is_sign_positive());
assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0);
assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative());
assert_eq!(($inf as $fty).maximum(9.0), $inf);
assert_eq!((9.0 as $fty).maximum($inf), $inf);
assert_eq!(($inf as $fty).maximum(-9.0), $inf);
assert_eq!((-9.0 as $fty).maximum($inf), $inf);
assert_eq!(($neginf as $fty).maximum(9.0), 9.0);
assert_eq!((9.0 as $fty).maximum($neginf), 9.0);
assert_eq!(($neginf as $fty).maximum(-9.0), -9.0);
assert_eq!((-9.0 as $fty).maximum($neginf), -9.0);
assert!(($nan as $fty).maximum(9.0).is_nan());
assert!(($nan as $fty).maximum(-9.0).is_nan());
assert!((9.0 as $fty).maximum($nan).is_nan());
assert!((-9.0 as $fty).maximum($nan).is_nan());
assert!(($nan as $fty).maximum($nan).is_nan());
}
#[test]
fn rem_euclid() {
let a: $fty = 42.0;
assert!($inf.rem_euclid(a).is_nan());

View File

@ -19,6 +19,18 @@ fn test_max_nan() {
assert_eq!(2.0f32.max(f32::NAN), 2.0);
}
#[test]
fn test_minimum() {
assert!(f32::NAN.minimum(2.0).is_nan());
assert!(2.0f32.minimum(f32::NAN).is_nan());
}
#[test]
fn test_maximum() {
assert!(f32::NAN.maximum(2.0).is_nan());
assert!(2.0f32.maximum(f32::NAN).is_nan());
}
#[test]
fn test_nan() {
let nan: f32 = f32::NAN;

View File

@ -287,6 +287,7 @@
#![feature(exhaustive_patterns)]
#![feature(extend_one)]
#![feature(fn_traits)]
#![feature(float_minimum_maximum)]
#![feature(format_args_nl)]
#![feature(gen_future)]
#![feature(generator_trait)]