about summary refs log tree commit diff
diff options
context:
space:
mode:
authorАртём Павлов <newpavlov@gmail.com>2022-04-14 20:44:09 +0300
committerАртём Павлов <newpavlov@gmail.com>2022-04-14 20:44:09 +0300
commit65c75b2db7827f8faa63c332139a3264b4ab9427 (patch)
treef7b3d928561bb0988a6f2916389598e717be0a04
parent563250a65c884c6767dc2ee2e2580360d33d561c (diff)
downloadrust-65c75b2db7827f8faa63c332139a3264b4ab9427.tar.gz
rust-65c75b2db7827f8faa63c332139a3264b4ab9427.zip
Use rounding in float to Duration conversion methods
-rw-r--r--library/core/src/time.rs61
1 files changed, 38 insertions, 23 deletions
diff --git a/library/core/src/time.rs b/library/core/src/time.rs
index 243c044b5d9..fc2484f0cdd 100644
--- a/library/core/src/time.rs
+++ b/library/core/src/time.rs
@@ -730,9 +730,9 @@ impl Duration {
     /// // subnormal float
     /// let res = Duration::from_secs_f64(f64::from_bits(1));
     /// assert_eq!(res, Duration::new(0, 0));
-    /// // conversion uses truncation, not rounding
+    /// // conversion uses rounding
     /// let res = Duration::from_secs_f64(0.999e-9);
-    /// assert_eq!(res, Duration::new(0, 0));
+    /// assert_eq!(res, Duration::new(0, 1));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use]
@@ -760,17 +760,17 @@ impl Duration {
     /// let res = Duration::from_secs_f32(1e-20);
     /// assert_eq!(res, Duration::new(0, 0));
     /// let res = Duration::from_secs_f32(4.2e-7);
-    /// assert_eq!(res, Duration::new(0, 419));
+    /// assert_eq!(res, Duration::new(0, 420));
     /// let res = Duration::from_secs_f32(2.7);
-    /// assert_eq!(res, Duration::new(2, 700_000_047));
+    /// assert_eq!(res, Duration::new(2, 700_000_048));
     /// let res = Duration::from_secs_f32(3e10);
     /// assert_eq!(res, Duration::new(30_000_001_024, 0));
     /// // subnormal float
     /// let res = Duration::from_secs_f32(f32::from_bits(1));
     /// assert_eq!(res, Duration::new(0, 0));
-    /// // conversion uses truncation, not rounding
+    /// // conversion uses rounding
     /// let res = Duration::from_secs_f32(0.999e-9);
-    /// assert_eq!(res, Duration::new(0, 0));
+    /// assert_eq!(res, Duration::new(0, 1));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use]
@@ -815,7 +815,7 @@ impl Duration {
     /// use std::time::Duration;
     ///
     /// let dur = Duration::new(2, 700_000_000);
-    /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640));
+    /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_641));
     /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847800, 0));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
@@ -838,8 +838,7 @@ impl Duration {
     ///
     /// let dur = Duration::new(2, 700_000_000);
     /// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611));
-    /// // note that truncation is used, not rounding
-    /// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_598));
+    /// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_599));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use = "this returns the result of the operation, \
@@ -862,9 +861,8 @@ impl Duration {
     /// let dur = Duration::new(2, 700_000_000);
     /// // note that due to rounding errors result is slightly
     /// // different from 0.859_872_611
-    /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_579));
-    /// // note that truncation is used, not rounding
-    /// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_598));
+    /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_580));
+    /// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_599));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use = "this returns the result of the operation, \
@@ -1278,13 +1276,30 @@ macro_rules! try_from_secs {
         } else if exp < 0 {
             // the input is less than 1 second
             let t = <$double_ty>::from(mant) << ($offset + exp);
-            let nanos = (u128::from(NANOS_PER_SEC) * u128::from(t)) >> ($mant_bits + $offset);
-            (0, nanos as u32)
+            let nanos_offset = $mant_bits + $offset;
+            let nanos_tmp = u128::from(NANOS_PER_SEC) * u128::from(t);
+            let nanos = (nanos_tmp >> nanos_offset) as u32;
+            if nanos_tmp & (1 << (nanos_offset - 1)) == 0 {
+                (0, nanos)
+            } else if nanos + 1 != NANOS_PER_SEC {
+                (0, nanos + 1)
+            } else {
+                (1, 0)
+            }
         } else if exp < $mant_bits {
-            let secs = mant >> ($mant_bits - exp);
+            let secs = u64::from(mant >> ($mant_bits - exp));
             let t = <$double_ty>::from((mant << exp) & MANT_MASK);
-            let nanos = (<$double_ty>::from(NANOS_PER_SEC) * t) >> $mant_bits;
-            (u64::from(secs), nanos as u32)
+            let nanos_tmp = <$double_ty>::from(NANOS_PER_SEC) * t;
+            let nanos = (nanos_tmp >> $mant_bits) as u32;
+            if nanos_tmp & (1 << ($mant_bits - 1)) == 0 {
+                (secs, nanos)
+            } else if nanos + 1 != NANOS_PER_SEC {
+                (secs, nanos + 1)
+            } else {
+                // `secs + 1` can not overflow since `exp` is less than `$mant_bits`
+                // and the latter is less than 64 bits for both `f32` and `f64`
+                (secs + 1, 0)
+            }
         } else if exp < 64 {
             // the input has no fractional part
             let secs = u64::from(mant) << (exp - $mant_bits);
@@ -1315,17 +1330,17 @@ impl Duration {
     /// let res = Duration::try_from_secs_f32(1e-20);
     /// assert_eq!(res, Ok(Duration::new(0, 0)));
     /// let res = Duration::try_from_secs_f32(4.2e-7);
-    /// assert_eq!(res, Ok(Duration::new(0, 419)));
+    /// assert_eq!(res, Ok(Duration::new(0, 420)));
     /// let res = Duration::try_from_secs_f32(2.7);
-    /// assert_eq!(res, Ok(Duration::new(2, 700_000_047)));
+    /// assert_eq!(res, Ok(Duration::new(2, 700_000_048)));
     /// let res = Duration::try_from_secs_f32(3e10);
     /// assert_eq!(res, Ok(Duration::new(30_000_001_024, 0)));
     /// // subnormal float:
     /// let res = Duration::try_from_secs_f32(f32::from_bits(1));
     /// assert_eq!(res, Ok(Duration::new(0, 0)));
-    /// // conversion uses truncation, not rounding
+    /// // conversion uses rounding
     /// let res = Duration::try_from_secs_f32(0.999e-9);
-    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    /// assert_eq!(res, Ok(Duration::new(0, 1)));
     ///
     /// let res = Duration::try_from_secs_f32(-5.0);
     /// assert!(res.is_err());
@@ -1372,9 +1387,9 @@ impl Duration {
     /// // subnormal float
     /// let res = Duration::try_from_secs_f64(f64::from_bits(1));
     /// assert_eq!(res, Ok(Duration::new(0, 0)));
-    /// // conversion uses truncation, not rounding
+    /// // conversion uses rounding
     /// let res = Duration::try_from_secs_f32(0.999e-9);
-    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    /// assert_eq!(res, Ok(Duration::new(0, 1)));
     ///
     /// let res = Duration::try_from_secs_f64(-5.0);
     /// assert!(res.is_err());