diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index db05d7fdc5087..510849c1ae040 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -2028,11 +2028,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 273ef3688ca5f..83bb13cc1ecbc 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -2014,11 +2014,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 d3dea38dc2fcf..52d289bf3ec9a 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -2166,11 +2166,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 7c5082749cd11..e197e39d6a3af 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -2144,11 +2144,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 b8f75489bb212..77b68c3148035 100644 --- a/library/coretests/tests/num/floats.rs +++ b/library/coretests/tests/num/floats.rs @@ -1001,6 +1001,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: {