diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 4875835695e69..8f7ab7078b1c5 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1867,11 +1867,21 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn div_euclid(self, rhs: f128) -> f128 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + // Use floor() directly as the initial guess (Euclidean division + // with positive divisor is the same as floor division). + let q = (self / rhs).floor(); + // Compute b * q - a with a single rounding error. + let diff = rhs.mul_add(q, -self); + if diff > 0.0 { // take care of NaN: NaN > 0.0 is false, keeping the return result. + if rhs > 0.0 { + // q was one unit too high – move down to the next representable value. + q.next_down().floor() + } else { + q.next_up().ceil() + } + } else { + q } - q } /// Calculates the least nonnegative remainder of `self` when diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 186945db9ae92..f6f69b0610e0e 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1853,11 +1853,21 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn div_euclid(self, rhs: f16) -> f16 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + // Use floor() directly as the initial guess (Euclidean division + // with positive divisor is the same as floor division). + let q = (self / rhs).floor(); + // Compute b * q - a with a single rounding error. + let diff = rhs.mul_add(q, -self); + if diff > 0.0 { // take care of NaN: NaN > 0.0 is false, keeping the return result. + if rhs > 0.0 { + // q was one unit too high – move down to the next representable value. + q.next_down().floor() + } else { + q.next_up().ceil() + } + } else { + q } - q } /// Calculates the least nonnegative remainder of `self` when diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 92ecabc581839..28cc0f98b38ff 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -2007,11 +2007,21 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn div_euclid(x: f32, rhs: f32) -> f32 { - let q = trunc(x / rhs); - if x % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + // Use floor() directly as the initial guess (Euclidean division + // with positive divisor is the same as floor division). + let q = (x / rhs).floor(); + // Compute b * q - a with a single rounding error. + let diff = rhs.mul_add(q, -x); + if diff > 0.0 { // take care of NaN: NaN > 0.0 is false, keeping the return result. + if rhs > 0.0 { + // q was one unit too high – move down to the next representable value. + q.next_down().floor() + } else { + q.next_up().ceil() + } + } else { + q } - q } /// Experimental version of `rem_euclid` in `core`. See [`f32::rem_euclid`] for details. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 6470658d3ea35..3ec353d494977 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1987,11 +1987,21 @@ pub mod math { #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn div_euclid(x: f64, rhs: f64) -> f64 { - let q = trunc(x / rhs); - if x % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + // Use floor() directly as the initial guess (Euclidean division + // with positive divisor is the same as floor division). + let q = (x / rhs).floor(); + // Compute b * q - a with a single rounding error. + let diff = rhs.mul_add(q, -x); + if diff > 0.0 { // take care of NaN: NaN > 0.0 is false, keeping the return result. + if rhs > 0.0 { + // q was one unit too high – move down to the next representable value. + q.next_down().floor() + } else { + q.next_up().ceil() + } + } else { + q } - q } /// Experimental version of `rem_euclid` in `core`. See [`f64::rem_euclid`] for details. diff --git a/library/coretests/tests/num/floats.rs b/library/coretests/tests/num/floats.rs index 1d7956b41c9d1..2e7c0bf7d4727 100644 --- a/library/coretests/tests/num/floats.rs +++ b/library/coretests/tests/num/floats.rs @@ -994,6 +994,22 @@ float_test! { } } +float_test! { + name: div_rem_euclid, + attrs: { + const: #[cfg(false)], + // Miri only uses softfloats here, so that always works + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_approx_eq!(flt(11.0).div_euclid(flt(2.2)) * flt(2.2) + flt(11.0).rem_euclid(flt(2.2)), flt(11.0)); + assert_approx_eq!(flt(12.0).div_euclid(flt(2.4)) * flt(2.4) + flt(12.0).rem_euclid(flt(2.4)), flt(12.0)); + assert_approx_eq!(flt(13.0).div_euclid(flt(2.6)) * flt(2.6) + flt(13.0).rem_euclid(flt(2.6)), flt(13.0)); + assert_approx_eq!(flt(14.0).div_euclid(flt(2.8)) * flt(2.8) + flt(14.0).rem_euclid(flt(2.8)), flt(14.0)); + } +} + float_test! { name: floor, attrs: {