Skip to content

Commit a8c470a

Browse files
committed
Auto merge of rust-lang#121062 - RustyYato:f32-midpoint, r=the8472
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.
2 parents 9cb6bb8 + 6aa391a commit a8c470a

File tree

2 files changed

+41
-19
lines changed

2 files changed

+41
-19
lines changed

library/core/src/num/f32.rs

+30-19
Original file line numberDiff line numberDiff line change
@@ -1030,25 +1030,36 @@ impl f32 {
10301030
/// ```
10311031
#[unstable(feature = "num_midpoint", issue = "110840")]
10321032
pub fn midpoint(self, other: f32) -> f32 {
1033-
const LO: f32 = f32::MIN_POSITIVE * 2.;
1034-
const HI: f32 = f32::MAX / 2.;
1035-
1036-
let (a, b) = (self, other);
1037-
let abs_a = a.abs_private();
1038-
let abs_b = b.abs_private();
1039-
1040-
if abs_a <= HI && abs_b <= HI {
1041-
// Overflow is impossible
1042-
(a + b) / 2.
1043-
} else if abs_a < LO {
1044-
// Not safe to halve a
1045-
a + (b / 2.)
1046-
} else if abs_b < LO {
1047-
// Not safe to halve b
1048-
(a / 2.) + b
1049-
} else {
1050-
// Not safe to halve a and b
1051-
(a / 2.) + (b / 2.)
1033+
cfg_if! {
1034+
if #[cfg(all(target_arch = "arm", target_pointer_width = "32",
1035+
not(target_feature = "vfp2")))] {
1036+
// some 32-bit ARM architectures don't have native double-precision floats
1037+
// so fall back to a similar algorithm as in f64, but using f32
1038+
// This should only differ in the specific NaNs reported.
1039+
1040+
const LO: f32 = f32::MIN_POSITIVE * 2.;
1041+
const HI: f32 = f32::MAX / 2.;
1042+
1043+
let (a, b) = (self, other);
1044+
let abs_a = a.abs_private();
1045+
let abs_b = b.abs_private();
1046+
1047+
if abs_a <= HI && abs_b <= HI {
1048+
// Overflow is impossible
1049+
(a + b) / 2.
1050+
} else if abs_a < LO {
1051+
// Not safe to halve a
1052+
a + (b / 2.)
1053+
} else if abs_b < LO {
1054+
// Not safe to halve b
1055+
(a / 2.) + b
1056+
} else {
1057+
// Not safe to halve a and b
1058+
(a / 2.) + (b / 2.)
1059+
}
1060+
} else {
1061+
((f64::from(self) + f64::from(other)) / 2.0) as f32
1062+
}
10521063
}
10531064
}
10541065

library/core/tests/num/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,17 @@ macro_rules! test_float {
880880
assert!(($nan as $fty).midpoint(1.0).is_nan());
881881
assert!((1.0 as $fty).midpoint($nan).is_nan());
882882
assert!(($nan as $fty).midpoint($nan).is_nan());
883+
884+
// test if large differences in magnitude are still correctly computed.
885+
// NOTE: that because of how small x and y are, x + y can never overflow
886+
// so (x + y) / 2.0 is always correct
887+
for i in 64..128 {
888+
for j in 0..64u8 {
889+
let x = (2.0f32.powi(i) + f32::from(j)) / 2.0;
890+
let y = 2.0f32.powi(i).midpoint(f32::from(j));
891+
assert_eq!(x, y);
892+
}
893+
}
883894
}
884895
#[test]
885896
fn rem_euclid() {

0 commit comments

Comments
 (0)