Implement midpoint for all floating point f32 and f64

This commit is contained in:
Loïc BRANSTETT 2021-12-17 20:01:19 +01:00 committed by Urgau
parent 1a72d7c7c4
commit bf73234d92
3 changed files with 125 additions and 3 deletions

View File

@ -940,6 +940,42 @@ impl f32 {
}
}
/// Calculates the middle point of `self` and `rhs`.
///
/// This returns NaN when *either* argument is NaN or if a combination of
/// +inf and -inf is provided as arguments.
///
/// # Examples
///
/// ```
/// #![feature(num_midpoint)]
/// assert_eq!(1f32.midpoint(4.0), 2.5);
/// assert_eq!((-5.5f32).midpoint(8.0), 1.25);
/// ```
#[unstable(feature = "num_midpoint", issue = "110840")]
pub fn midpoint(self, other: f32) -> f32 {
const LO: f32 = f32::MIN_POSITIVE * 2.;
const HI: f32 = f32::MAX / 2.;
let (a, b) = (self, other);
let abs_a = a.abs_private();
let abs_b = b.abs_private();
if abs_a <= HI && abs_b <= HI {
// Overflow is impossible
(a + b) / 2.
} else if abs_a < LO {
// Not safe to halve a
a + (b / 2.)
} else if abs_b < LO {
// Not safe to halve b
(a / 2.) + b
} else {
// Not safe to halve a and b
(a / 2.) + (b / 2.)
}
}
/// Rounds toward zero and converts to any primitive integer type,
/// assuming that the value is finite and fits in that type.
///

View File

@ -951,6 +951,42 @@ impl f64 {
}
}
/// Calculates the middle point of `self` and `rhs`.
///
/// This returns NaN when *either* argument is NaN or if a combination of
/// +inf and -inf is provided as arguments.
///
/// # Examples
///
/// ```
/// #![feature(num_midpoint)]
/// assert_eq!(1f64.midpoint(4.0), 2.5);
/// assert_eq!((-5.5f64).midpoint(8.0), 1.25);
/// ```
#[unstable(feature = "num_midpoint", issue = "110840")]
pub fn midpoint(self, other: f64) -> f64 {
const LO: f64 = f64::MIN_POSITIVE * 2.;
const HI: f64 = f64::MAX / 2.;
let (a, b) = (self, other);
let abs_a = a.abs_private();
let abs_b = b.abs_private();
if abs_a <= HI && abs_b <= HI {
// Overflow is impossible
(a + b) / 2.
} else if abs_a < LO {
// Not safe to halve a
a + (b / 2.)
} else if abs_b < LO {
// Not safe to halve b
(a / 2.) + b
} else {
// Not safe to halve a and b
(a / 2.) + (b / 2.)
}
}
/// Rounds toward zero and converts to any primitive integer type,
/// assuming that the value is finite and fits in that type.
///

View File

@ -724,7 +724,7 @@ assume_usize_width! {
}
macro_rules! test_float {
($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr) => {
($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr) => {
mod $modname {
#[test]
fn min() {
@ -845,6 +845,38 @@ macro_rules! test_float {
assert!(($nan as $fty).maximum($nan).is_nan());
}
#[test]
fn midpoint() {
assert_eq!((0.5 as $fty).midpoint(0.5), 0.5);
assert_eq!((0.5 as $fty).midpoint(2.5), 1.5);
assert_eq!((3.0 as $fty).midpoint(4.0), 3.5);
assert_eq!((-3.0 as $fty).midpoint(4.0), 0.5);
assert_eq!((3.0 as $fty).midpoint(-4.0), -0.5);
assert_eq!((-3.0 as $fty).midpoint(-4.0), -3.5);
assert_eq!((0.0 as $fty).midpoint(0.0), 0.0);
assert_eq!((-0.0 as $fty).midpoint(-0.0), -0.0);
assert_eq!((-5.0 as $fty).midpoint(5.0), 0.0);
assert_eq!(($max as $fty).midpoint($min), 0.0);
assert_eq!(($min as $fty).midpoint($max), -0.0);
assert_eq!(($max as $fty).midpoint($min_pos), $max / 2.);
assert_eq!((-$max as $fty).midpoint($min_pos), -$max / 2.);
assert_eq!(($max as $fty).midpoint(-$min_pos), $max / 2.);
assert_eq!((-$max as $fty).midpoint(-$min_pos), -$max / 2.);
assert_eq!(($min_pos as $fty).midpoint($max), $max / 2.);
assert_eq!(($min_pos as $fty).midpoint(-$max), -$max / 2.);
assert_eq!((-$min_pos as $fty).midpoint($max), $max / 2.);
assert_eq!((-$min_pos as $fty).midpoint(-$max), -$max / 2.);
assert_eq!(($max as $fty).midpoint($max), $max);
assert_eq!(($min_pos as $fty).midpoint($min_pos), $min_pos);
assert_eq!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos);
assert_eq!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5);
assert_eq!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5);
assert_eq!(($inf as $fty).midpoint($inf), $inf);
assert_eq!(($neginf as $fty).midpoint($neginf), $neginf);
assert!(($nan as $fty).midpoint(1.0).is_nan());
assert!((1.0 as $fty).midpoint($nan).is_nan());
assert!(($nan as $fty).midpoint($nan).is_nan());
}
#[test]
fn rem_euclid() {
let a: $fty = 42.0;
assert!($inf.rem_euclid(a).is_nan());
@ -867,5 +899,23 @@ macro_rules! test_float {
};
}
test_float!(f32, f32, f32::INFINITY, f32::NEG_INFINITY, f32::NAN);
test_float!(f64, f64, f64::INFINITY, f64::NEG_INFINITY, f64::NAN);
test_float!(
f32,
f32,
f32::INFINITY,
f32::NEG_INFINITY,
f32::NAN,
f32::MIN,
f32::MAX,
f32::MIN_POSITIVE
);
test_float!(
f64,
f64,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NAN,
f64::MIN,
f64::MAX,
f64::MIN_POSITIVE
);