Rollup merge of #121062 - RustyYato:f32-midpoint, r=the8472
Change f32::midpoint to upcast to f64 This has been verified by kani as a correct optimization see: https://github.com/rust-lang/rust/issues/110840#issuecomment-1942587398 The new implementation is branchless and only differs in which NaN values are produced (if any are produced at all), which is fine to change. Aside from NaN handling, this implementation produces bitwise identical results to the original implementation. Question: do we need a codegen test for this? I didn't add one, since the original PR #92048 didn't have any codegen tests.
This commit is contained in:
commit
713cdcd803
2 changed files with 60 additions and 20 deletions
|
@ -1030,6 +1030,21 @@ impl f32 {
|
||||||
/// ```
|
/// ```
|
||||||
#[unstable(feature = "num_midpoint", issue = "110840")]
|
#[unstable(feature = "num_midpoint", issue = "110840")]
|
||||||
pub fn midpoint(self, other: f32) -> f32 {
|
pub fn midpoint(self, other: f32) -> f32 {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(any(
|
||||||
|
target_arch = "x86_64",
|
||||||
|
target_arch = "aarch64",
|
||||||
|
all(any(target_arch="riscv32", target_arch= "riscv64"), target_feature="d"),
|
||||||
|
all(target_arch = "arm", target_feature="vfp2"),
|
||||||
|
target_arch = "wasm32",
|
||||||
|
target_arch = "wasm64",
|
||||||
|
))] {
|
||||||
|
// whitelist the faster implementation to targets that have known good 64-bit float
|
||||||
|
// implementations. Falling back to the branchy code on targets that don't have
|
||||||
|
// 64-bit hardware floats or buggy implementations.
|
||||||
|
// see: https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114
|
||||||
|
((f64::from(self) + f64::from(other)) / 2.0) as f32
|
||||||
|
} else {
|
||||||
const LO: f32 = f32::MIN_POSITIVE * 2.;
|
const LO: f32 = f32::MIN_POSITIVE * 2.;
|
||||||
const HI: f32 = f32::MAX / 2.;
|
const HI: f32 = f32::MAX / 2.;
|
||||||
|
|
||||||
|
@ -1051,6 +1066,8 @@ impl f32 {
|
||||||
(a / 2.) + (b / 2.)
|
(a / 2.) + (b / 2.)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Rounds toward zero and converts to any primitive integer type,
|
/// Rounds toward zero and converts to any primitive integer type,
|
||||||
/// assuming that the value is finite and fits in that type.
|
/// assuming that the value is finite and fits in that type.
|
||||||
|
|
|
@ -729,7 +729,7 @@ assume_usize_width! {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test_float {
|
macro_rules! test_float {
|
||||||
($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr) => {
|
($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => {
|
||||||
mod $modname {
|
mod $modname {
|
||||||
#[test]
|
#[test]
|
||||||
fn min() {
|
fn min() {
|
||||||
|
@ -880,6 +880,27 @@ macro_rules! test_float {
|
||||||
assert!(($nan as $fty).midpoint(1.0).is_nan());
|
assert!(($nan as $fty).midpoint(1.0).is_nan());
|
||||||
assert!((1.0 as $fty).midpoint($nan).is_nan());
|
assert!((1.0 as $fty).midpoint($nan).is_nan());
|
||||||
assert!(($nan as $fty).midpoint($nan).is_nan());
|
assert!(($nan as $fty).midpoint($nan).is_nan());
|
||||||
|
|
||||||
|
// test if large differences in magnitude are still correctly computed.
|
||||||
|
// NOTE: that because of how small x and y are, x + y can never overflow
|
||||||
|
// so (x + y) / 2.0 is always correct
|
||||||
|
// in particular, `2.pow(i)` will never be at the max exponent, so it could
|
||||||
|
// be safely doubled, while j is significantly smaller.
|
||||||
|
for i in $max_exp.saturating_sub(64)..$max_exp {
|
||||||
|
for j in 0..64u8 {
|
||||||
|
let large = <$fty>::from(2.0f32).powi(i);
|
||||||
|
// a much smaller number, such that there is no chance of overflow to test
|
||||||
|
// potential double rounding in midpoint's implementation.
|
||||||
|
let small = <$fty>::from(2.0f32).powi($max_exp - 1)
|
||||||
|
* <$fty>::EPSILON
|
||||||
|
* <$fty>::from(j);
|
||||||
|
|
||||||
|
let naive = (large + small) / 2.0;
|
||||||
|
let midpoint = large.midpoint(small);
|
||||||
|
|
||||||
|
assert_eq!(naive, midpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn rem_euclid() {
|
fn rem_euclid() {
|
||||||
|
@ -912,7 +933,8 @@ test_float!(
|
||||||
f32::NAN,
|
f32::NAN,
|
||||||
f32::MIN,
|
f32::MIN,
|
||||||
f32::MAX,
|
f32::MAX,
|
||||||
f32::MIN_POSITIVE
|
f32::MIN_POSITIVE,
|
||||||
|
f32::MAX_EXP
|
||||||
);
|
);
|
||||||
test_float!(
|
test_float!(
|
||||||
f64,
|
f64,
|
||||||
|
@ -922,5 +944,6 @@ test_float!(
|
||||||
f64::NAN,
|
f64::NAN,
|
||||||
f64::MIN,
|
f64::MIN,
|
||||||
f64::MAX,
|
f64::MAX,
|
||||||
f64::MIN_POSITIVE
|
f64::MIN_POSITIVE,
|
||||||
|
f64::MAX_EXP
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue