Skip to content

Commit

Permalink
Auto merge of rust-lang#121062 - RustyYato:f32-midpoint, r=the8472
Browse files Browse the repository at this point in the history
Change f32::midpoint to upcast to f64

This has been verified by kani as a correct optimization

see: rust-lang#110840 (comment)

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 rust-lang#92048 didn't have any codegen tests.
  • Loading branch information
bors committed May 21, 2024
2 parents 9cb6bb8 + 6aa391a commit a8c470a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 19 deletions.
49 changes: 30 additions & 19 deletions library/core/src/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1030,25 +1030,36 @@ impl f32 {
/// ```
#[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.)
cfg_if! {
if #[cfg(all(target_arch = "arm", target_pointer_width = "32",
not(target_feature = "vfp2")))] {
// some 32-bit ARM architectures don't have native double-precision floats
// so fall back to a similar algorithm as in f64, but using f32
// This should only differ in the specific NaNs reported.

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.)
}
} else {
((f64::from(self) + f64::from(other)) / 2.0) as f32
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions library/core/tests/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,17 @@ macro_rules! test_float {
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 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
for i in 64..128 {
for j in 0..64u8 {
let x = (2.0f32.powi(i) + f32::from(j)) / 2.0;
let y = 2.0f32.powi(i).midpoint(f32::from(j));
assert_eq!(x, y);
}
}
}
#[test]
fn rem_euclid() {
Expand Down

0 comments on commit a8c470a

Please sign in to comment.