From 2356e3e127e338505f61e649667b981c257bab7f Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 26 Nov 2024 05:00:15 +0000 Subject: [PATCH] Reimplements float::div_euclid by copying Python implementation FIXME: The Python link uses "fmod " to get more exact remander. But this PR only uses raw % operator. Is this a problem in practice? Reference: * . --- library/std/src/f128.rs | 31 ++++++++++++++++++++++++++----- library/std/src/f16.rs | 31 ++++++++++++++++++++++++++----- library/std/src/f32.rs | 31 ++++++++++++++++++++++++++----- library/std/src/f64.rs | 31 ++++++++++++++++++++++++++----- 4 files changed, 104 insertions(+), 20 deletions(-) diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index e93e915159e40..6b99edfe0a705 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -255,8 +255,12 @@ impl f128 { /// let b = 4.0; /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(a.div_euclid(-b), -2.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 1.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(11f128.div_euclid(1.1), 9.0); + /// assert_eq!((-11f128).div_euclid(1.1), -10.0); + /// assert_eq!(0.5f128.div_euclid(1.1), 0.0); + /// assert_eq!((-0.5f128).div_euclid(1.1), -1.0); /// # } /// ``` #[inline] @@ -264,10 +268,27 @@ 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 }; + // Copied from Python implementation: + // NOTE: Should we use `fmod` instead? + let rem = self % rhs; + let mut div = (self - rem) / rhs; + if rem != 0.0 { + /* ensure the remainder has the same sign as the denominator */ + if (rhs < 0.0) != (rem < 0.0) { + div -= 1.0; + } } + /* snap quotient to nearest integral value */ + let q = if div != 0.0 { + let mut q = div.floor(); + if div - q > 0.5 { + q += 1.0; + } + q + } else { + /* div is zero - get the same sign as the true quotient */ + Self::copysign(0.0, self / rhs) + }; q } diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 5b7fcaa28e064..e4dd474c55537 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -255,8 +255,12 @@ impl f16 { /// let b = 4.0; /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(a.div_euclid(-b), -2.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 1.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(11f16.div_euclid(1.1), 9.0); + /// assert_eq!((-11f16).div_euclid(1.1), -10.0); + /// assert_eq!(0.5f16.div_euclid(1.1), 0.0); + /// assert_eq!(-0.5f16.div_euclid(1.1), -1.0); /// # } /// ``` #[inline] @@ -264,10 +268,27 @@ 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 }; + // Copied from Python implementation: + // NOTE: Should we use `fmod` instead? + let rem = self % rhs; + let mut div = (self - rem) / rhs; + if rem != 0.0 { + /* ensure the remainder has the same sign as the denominator */ + if (rhs < 0.0) != (rem < 0.0) { + div -= 1.0; + } } + /* snap quotient to nearest integral value */ + let q = if div != 0.0 { + let mut q = div.floor(); + if div - q > 0.5 { + q += 1.0; + } + q + } else { + /* div is zero - get the same sign as the true quotient */ + Self::copysign(0.0, self / rhs) + }; q } diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 7cb285bbff5f7..63581f58541e0 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -236,18 +236,39 @@ impl f32 { /// let b = 4.0; /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(a.div_euclid(-b), -2.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 1.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(11f32.div_euclid(1.1), 9.0); + /// assert_eq!((-11f32).div_euclid(1.1), -10.0); + /// assert_eq!(0.5f32.div_euclid(1.1), 0.0); + /// assert_eq!((-0.5f32).div_euclid(1.1), -1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f32) -> f32 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + // Copied from Python implementation: + // NOTE: Should we use `fmod` instead? + let rem = self % rhs; + let mut div = (self - rem) / rhs; + if rem != 0.0 { + /* ensure the remainder has the same sign as the denominator */ + if (rhs < 0.0) != (rem < 0.0) { + div -= 1.0; + } } + /* snap quotient to nearest integral value */ + let q = if div != 0.0 { + let mut q = div.floor(); + if div - q > 0.5 { + q += 1.0; + } + q + } else { + /* div is zero - get the same sign as the true quotient */ + Self::copysign(0.0, self / rhs) + }; q } diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 47163c272de32..bc243a8629acf 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -236,18 +236,39 @@ impl f64 { /// let b = 4.0; /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(a.div_euclid(-b), -2.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 1.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(11f64.div_euclid(1.1), 9.0); + /// assert_eq!((-11f64).div_euclid(1.1), -10.0); + /// assert_eq!(0.5f64.div_euclid(1.1), 0.0); + /// assert_eq!((-0.5f64).div_euclid(1.1), -1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[inline] #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f64) -> f64 { - let q = (self / rhs).trunc(); - if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + // Copied from Python implementation: + // NOTE: Should we use `fmod` instead? + let rem = self % rhs; + let mut div = (self - rem) / rhs; + if rem != 0.0 { + /* ensure the remainder has the same sign as the denominator */ + if (rhs < 0.0) != (rem < 0.0) { + div -= 1.0; + } } + /* snap quotient to nearest integral value */ + let q = if div != 0.0 { + let mut q = div.floor(); + if div - q > 0.5 { + q += 1.0; + } + q + } else { + /* div is zero - get the same sign as the true quotient */ + Self::copysign(0.0, self / rhs) + }; q }