]> git.lizzy.rs Git - rust.git/blobdiff - library/core/src/time.rs
Rollup merge of #92887 - pietroalbini:pa-bootstrap-update, r=Mark-Simulacrum
[rust.git] / library / core / src / time.rs
index 746d1cacfd0fb050071f7365409f0ca9354dd17f..243c044b5d9d0a55ee918885637f9177d49226e7 100644 (file)
@@ -711,14 +711,28 @@ pub const fn as_secs_f32(&self) -> f32 {
     /// as `f64`.
     ///
     /// # Panics
-    /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`.
+    /// This constructor will panic if `secs` is negative, overflows `Duration` or not finite.
     ///
     /// # Examples
     /// ```
     /// use std::time::Duration;
     ///
-    /// let dur = Duration::from_secs_f64(2.7);
-    /// assert_eq!(dur, Duration::new(2, 700_000_000));
+    /// let res = Duration::from_secs_f64(0.0);
+    /// assert_eq!(res, Duration::new(0, 0));
+    /// let res = Duration::from_secs_f64(1e-20);
+    /// assert_eq!(res, Duration::new(0, 0));
+    /// let res = Duration::from_secs_f64(4.2e-7);
+    /// assert_eq!(res, Duration::new(0, 420));
+    /// let res = Duration::from_secs_f64(2.7);
+    /// assert_eq!(res, Duration::new(2, 700_000_000));
+    /// let res = Duration::from_secs_f64(3e10);
+    /// assert_eq!(res, Duration::new(30_000_000_000, 0));
+    /// // subnormal float
+    /// let res = Duration::from_secs_f64(f64::from_bits(1));
+    /// assert_eq!(res, Duration::new(0, 0));
+    /// // conversion uses truncation, not rounding
+    /// let res = Duration::from_secs_f64(0.999e-9);
+    /// assert_eq!(res, Duration::new(0, 0));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use]
@@ -731,55 +745,32 @@ pub const fn from_secs_f64(secs: f64) -> Duration {
         }
     }
 
-    /// The checked version of [`from_secs_f64`].
-    ///
-    /// [`from_secs_f64`]: Duration::from_secs_f64
-    ///
-    /// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`.
-    ///
-    /// # Examples
-    /// ```
-    /// #![feature(duration_checked_float)]
-    /// use std::time::Duration;
-    ///
-    /// let dur = Duration::try_from_secs_f64(2.7);
-    /// assert_eq!(dur, Ok(Duration::new(2, 700_000_000)));
-    ///
-    /// let negative = Duration::try_from_secs_f64(-5.0);
-    /// assert!(negative.is_err());
-    /// ```
-    #[unstable(feature = "duration_checked_float", issue = "83400")]
-    #[inline]
-    pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, FromSecsError> {
-        const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64;
-        let nanos = secs * (NANOS_PER_SEC as f64);
-        if !nanos.is_finite() {
-            Err(FromSecsError { kind: FromSecsErrorKind::NonFinite })
-        } else if nanos >= MAX_NANOS_F64 {
-            Err(FromSecsError { kind: FromSecsErrorKind::Overflow })
-        } else if nanos < 0.0 {
-            Err(FromSecsError { kind: FromSecsErrorKind::Negative })
-        } else {
-            let nanos = nanos as u128;
-            Ok(Duration {
-                secs: (nanos / (NANOS_PER_SEC as u128)) as u64,
-                nanos: (nanos % (NANOS_PER_SEC as u128)) as u32,
-            })
-        }
-    }
-
     /// Creates a new `Duration` from the specified number of seconds represented
     /// as `f32`.
     ///
     /// # Panics
-    /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`.
+    /// This constructor will panic if `secs` is negative, overflows `Duration` or not finite.
     ///
     /// # Examples
     /// ```
     /// use std::time::Duration;
     ///
-    /// let dur = Duration::from_secs_f32(2.7);
-    /// assert_eq!(dur, Duration::new(2, 700_000_000));
+    /// let res = Duration::from_secs_f32(0.0);
+    /// assert_eq!(res, Duration::new(0, 0));
+    /// 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));
+    /// let res = Duration::from_secs_f32(2.7);
+    /// assert_eq!(res, Duration::new(2, 700_000_047));
+    /// 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
+    /// let res = Duration::from_secs_f32(0.999e-9);
+    /// assert_eq!(res, Duration::new(0, 0));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use]
@@ -792,47 +783,10 @@ pub const fn from_secs_f32(secs: f32) -> Duration {
         }
     }
 
-    /// The checked version of [`from_secs_f32`].
-    ///
-    /// [`from_secs_f32`]: Duration::from_secs_f32
-    ///
-    /// This constructor will return an `Err` if `secs` is not finite, negative or overflows `Duration`.
-    ///
-    /// # Examples
-    /// ```
-    /// #![feature(duration_checked_float)]
-    /// use std::time::Duration;
-    ///
-    /// let dur = Duration::try_from_secs_f32(2.7);
-    /// assert_eq!(dur, Ok(Duration::new(2, 700_000_000)));
-    ///
-    /// let negative = Duration::try_from_secs_f32(-5.0);
-    /// assert!(negative.is_err());
-    /// ```
-    #[unstable(feature = "duration_checked_float", issue = "83400")]
-    #[inline]
-    pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, FromSecsError> {
-        const MAX_NANOS_F32: f32 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f32;
-        let nanos = secs * (NANOS_PER_SEC as f32);
-        if !nanos.is_finite() {
-            Err(FromSecsError { kind: FromSecsErrorKind::NonFinite })
-        } else if nanos >= MAX_NANOS_F32 {
-            Err(FromSecsError { kind: FromSecsErrorKind::Overflow })
-        } else if nanos < 0.0 {
-            Err(FromSecsError { kind: FromSecsErrorKind::Negative })
-        } else {
-            let nanos = nanos as u128;
-            Ok(Duration {
-                secs: (nanos / (NANOS_PER_SEC as u128)) as u64,
-                nanos: (nanos % (NANOS_PER_SEC as u128)) as u32,
-            })
-        }
-    }
-
     /// Multiplies `Duration` by `f64`.
     ///
     /// # Panics
-    /// This method will panic if result is not finite, negative or overflows `Duration`.
+    /// This method will panic if result is negative, overflows `Duration` or not finite.
     ///
     /// # Examples
     /// ```
@@ -854,17 +808,15 @@ pub const fn mul_f64(self, rhs: f64) -> Duration {
     /// Multiplies `Duration` by `f32`.
     ///
     /// # Panics
-    /// This method will panic if result is not finite, negative or overflows `Duration`.
+    /// This method will panic if result is negative, overflows `Duration` or not finite.
     ///
     /// # Examples
     /// ```
     /// use std::time::Duration;
     ///
     /// let dur = Duration::new(2, 700_000_000);
-    /// // note that due to rounding errors result is slightly different
-    /// // from 8.478 and 847800.0
     /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640));
-    /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847799, 969_120_256));
+    /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847800, 0));
     /// ```
     #[stable(feature = "duration_float", since = "1.38.0")]
     #[must_use = "this returns the result of the operation, \
@@ -878,7 +830,7 @@ pub const fn mul_f32(self, rhs: f32) -> Duration {
     /// Divide `Duration` by `f64`.
     ///
     /// # Panics
-    /// This method will panic if result is not finite, negative or overflows `Duration`.
+    /// This method will panic if result is negative, overflows `Duration` or not finite.
     ///
     /// # Examples
     /// ```
@@ -901,7 +853,7 @@ pub const fn div_f64(self, rhs: f64) -> Duration {
     /// Divide `Duration` by `f32`.
     ///
     /// # Panics
-    /// This method will panic if result is not finite, negative or overflows `Duration`.
+    /// This method will panic if result is negative, overflows `Duration` or not finite.
     ///
     /// # Examples
     /// ```
@@ -910,7 +862,7 @@ pub const fn div_f64(self, rhs: f64) -> 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_576));
+    /// 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));
     /// ```
@@ -1267,33 +1219,180 @@ fn fmt_decimal(
 /// ```
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[unstable(feature = "duration_checked_float", issue = "83400")]
-pub struct FromSecsError {
-    kind: FromSecsErrorKind,
+pub struct FromFloatSecsError {
+    kind: FromFloatSecsErrorKind,
 }
 
-impl FromSecsError {
+impl FromFloatSecsError {
     const fn description(&self) -> &'static str {
         match self.kind {
-            FromSecsErrorKind::NonFinite => "non-finite value when converting float to duration",
-            FromSecsErrorKind::Overflow => "overflow when converting float to duration",
-            FromSecsErrorKind::Negative => "negative value when converting float to duration",
+            FromFloatSecsErrorKind::Negative => {
+                "can not convert float seconds to Duration: value is negative"
+            }
+            FromFloatSecsErrorKind::OverflowOrNan => {
+                "can not convert float seconds to Duration: value is either too big or NaN"
+            }
         }
     }
 }
 
 #[unstable(feature = "duration_checked_float", issue = "83400")]
-impl fmt::Display for FromSecsError {
+impl fmt::Display for FromFloatSecsError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self.description(), f)
+        self.description().fmt(f)
     }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
-enum FromSecsErrorKind {
-    // Value is not a finite value (either + or - infinity or NaN).
-    NonFinite,
-    // Value is too large to store in a `Duration`.
-    Overflow,
+enum FromFloatSecsErrorKind {
     // Value is negative.
     Negative,
+    // Value is either too big to be represented as `Duration` or `NaN`.
+    OverflowOrNan,
+}
+
+macro_rules! try_from_secs {
+    (
+        secs = $secs: expr,
+        mantissa_bits = $mant_bits: literal,
+        exponent_bits = $exp_bits: literal,
+        offset = $offset: literal,
+        bits_ty = $bits_ty:ty,
+        double_ty = $double_ty:ty,
+    ) => {{
+        const MIN_EXP: i16 = 1 - (1i16 << $exp_bits) / 2;
+        const MANT_MASK: $bits_ty = (1 << $mant_bits) - 1;
+        const EXP_MASK: $bits_ty = (1 << $exp_bits) - 1;
+
+        if $secs.is_sign_negative() {
+            return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::Negative });
+        }
+
+        let bits = $secs.to_bits();
+        let mant = (bits & MANT_MASK) | (MANT_MASK + 1);
+        let exp = ((bits >> $mant_bits) & EXP_MASK) as i16 + MIN_EXP;
+
+        let (secs, nanos) = if exp < -30 {
+            // the input represents less than 1ns.
+            (0u64, 0u32)
+        } 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)
+        } else if exp < $mant_bits {
+            let secs = 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)
+        } else if exp < 64 {
+            // the input has no fractional part
+            let secs = u64::from(mant) << (exp - $mant_bits);
+            (secs, 0)
+        } else {
+            return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::OverflowOrNan });
+        };
+
+        Ok(Duration { secs, nanos })
+    }};
+}
+
+impl Duration {
+    /// The checked version of [`from_secs_f32`].
+    ///
+    /// [`from_secs_f32`]: Duration::from_secs_f32
+    ///
+    /// This constructor will return an `Err` if `secs` is negative, overflows `Duration` or not finite.
+    ///
+    /// # Examples
+    /// ```
+    /// #![feature(duration_checked_float)]
+    ///
+    /// use std::time::Duration;
+    ///
+    /// let res = Duration::try_from_secs_f32(0.0);
+    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    /// 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)));
+    /// let res = Duration::try_from_secs_f32(2.7);
+    /// assert_eq!(res, Ok(Duration::new(2, 700_000_047)));
+    /// 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
+    /// let res = Duration::try_from_secs_f32(0.999e-9);
+    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    ///
+    /// let res = Duration::try_from_secs_f32(-5.0);
+    /// assert!(res.is_err());
+    /// let res = Duration::try_from_secs_f32(f32::NAN);
+    /// assert!(res.is_err());
+    /// let res = Duration::try_from_secs_f32(2e19);
+    /// assert!(res.is_err());
+    /// ```
+    #[unstable(feature = "duration_checked_float", issue = "83400")]
+    #[inline]
+    pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, FromFloatSecsError> {
+        try_from_secs!(
+            secs = secs,
+            mantissa_bits = 23,
+            exponent_bits = 8,
+            offset = 41,
+            bits_ty = u32,
+            double_ty = u64,
+        )
+    }
+
+    /// The checked version of [`from_secs_f64`].
+    ///
+    /// [`from_secs_f64`]: Duration::from_secs_f64
+    ///
+    /// This constructor will return an `Err` if `secs` is negative, overflows `Duration` or not finite.
+    ///
+    /// # Examples
+    /// ```
+    /// #![feature(duration_checked_float)]
+    ///
+    /// use std::time::Duration;
+    ///
+    /// let res = Duration::try_from_secs_f64(0.0);
+    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    /// let res = Duration::try_from_secs_f64(1e-20);
+    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    /// let res = Duration::try_from_secs_f64(4.2e-7);
+    /// assert_eq!(res, Ok(Duration::new(0, 420)));
+    /// let res = Duration::try_from_secs_f64(2.7);
+    /// assert_eq!(res, Ok(Duration::new(2, 700_000_000)));
+    /// let res = Duration::try_from_secs_f64(3e10);
+    /// assert_eq!(res, Ok(Duration::new(30_000_000_000, 0)));
+    /// // 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
+    /// let res = Duration::try_from_secs_f32(0.999e-9);
+    /// assert_eq!(res, Ok(Duration::new(0, 0)));
+    ///
+    /// let res = Duration::try_from_secs_f64(-5.0);
+    /// assert!(res.is_err());
+    /// let res = Duration::try_from_secs_f64(f64::NAN);
+    /// assert!(res.is_err());
+    /// let res = Duration::try_from_secs_f64(2e19);
+    /// assert!(res.is_err());
+    /// ```
+    #[unstable(feature = "duration_checked_float", issue = "83400")]
+    #[inline]
+    pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, FromFloatSecsError> {
+        try_from_secs!(
+            secs = secs,
+            mantissa_bits = 52,
+            exponent_bits = 11,
+            offset = 44,
+            bits_ty = u64,
+            double_ty = u128,
+        )
+    }
 }