Change:
} } <del> /// 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, }) } } </del> /// Creates a new `Duration` from the specified number of seconds represented /// as `f32`. /// /// # Panics <del> /// This constructor will panic if `secs` is not finite, negative or overflows `Duration`. </del> <ins> /// This constructor will panic if `secs` is negative, overflows `Duration` or not finite. </ins> /// /// # Examples /// ``` /// use std::time::Duration; /// <del> /// let dur = Duration::from_secs_f32(2.7); /// assert_eq!(dur, Duration::new(2, 700_000_000)); </del> <ins> /// 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)); </ins> /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use]

Discussion:
Hello. I am diagnosing why I've got a wrong duration computed from Duration::fromsecsf32()... I tried this code: I expected to see an exact 30-second duration. Instead, I've got this: which is: 30 seconds and 1024 nanos. I dig deeper into rust code and I've prepared a sample code which is computing it based on the source code: As the result I've got: Instead of exact 30 seconds and 0 nanos.
?
Works on current stable and nightly: I mean, that this issue exist for that versions.
yep, I also confirmed on:
What is interesting the function: fromsecsf64 doesn't seem to have this problem... so a quick workaround would be use it and cast from f32 to f64
Extending(?) f32 to u128 gives this addition in 1024:
CC: CC: Adding authors ... guys please take a quick look if you have a time...
f32 will naturally have trouble representing all possible nanosecond values and so doing calculations in f64 may help here somewhat. However the most appropriate approach would be to decompose the float into the whole part and the fractional part and then operate on the two separately to produce the values necessary to create the . and may work sufficiently well for the purpose. EDIT: I think we would still need to be careful about how the nanosecond fraction is handled. We cannot simply multiply the number by and then convert to an integer, because is not capable of representing integers of such degree losslessly.
We cannot simply multiply the number by and then convert to an integer, because f32 is not capable of representing integers of such degree losslessly. Small correction: is represented exactly by , the problem is with . The closest float is , which results in the 1024 ns error. For example, would have produced an exact value. I would say such loss of precision is expected for and if you want exact values, you should use the integer based methods. So I don't think this issue should be marked as a bug. It could be worthwhile to improve the code by processing the fractional part separately, but note that it will not solve this issue fundamentally. We still have the issue of mapping at most 23 fractional bits into ~30 bits, e.g. calling would still result in an inexact result.

specifically may be representable exactly, but f32 cannot represent all integers exactly. Though maybe its not a huge deal if after we end up roundtripping back to the actual integer in all cases.
Argh, I forgot that the method is not available on , so the linked PR will not work out of box. UPD: I have implemented manual fraction/exponent splitting and their conversion to integers.