@@ -1270,36 +1270,46 @@ macro_rules! try_from_secs {
12701270 let mant = ( bits & MANT_MASK ) | ( MANT_MASK + 1 ) ;
12711271 let exp = ( ( bits >> $mant_bits) & EXP_MASK ) as i16 + MIN_EXP ;
12721272
1273- let ( secs, nanos) = if exp < -30 {
1274- // the input represents less than 1ns.
1273+ let ( secs, nanos) = if exp < -31 {
1274+ // the input represents less than 1ns and can not be rounded to it
12751275 ( 0u64 , 0u32 )
12761276 } else if exp < 0 {
12771277 // the input is less than 1 second
12781278 let t = <$double_ty>:: from( mant) << ( $offset + exp) ;
12791279 let nanos_offset = $mant_bits + $offset;
12801280 let nanos_tmp = u128 :: from( NANOS_PER_SEC ) * u128 :: from( t) ;
12811281 let nanos = ( nanos_tmp >> nanos_offset) as u32 ;
1282- if nanos_tmp & ( 1 << ( nanos_offset - 1 ) ) == 0 {
1283- ( 0 , nanos)
1284- } else if nanos + 1 != NANOS_PER_SEC {
1285- ( 0 , nanos + 1 )
1286- } else {
1287- ( 1 , 0 )
1288- }
1282+
1283+ let rem_mask = ( 1 << nanos_offset) - 1 ;
1284+ let rem_msb_mask = 1 << ( nanos_offset - 1 ) ;
1285+ let rem = nanos_tmp & rem_mask;
1286+ let is_tie = rem == rem_msb_mask;
1287+ let is_even = ( nanos & 1 ) == 0 ;
1288+ let rem_msb = nanos_tmp & rem_msb_mask == 0 ;
1289+ let add_ns = !( rem_msb || ( is_even && is_tie) ) ;
1290+
1291+ // note that neither `f32`, nor `f64` can represent
1292+ // 0.999_999_999_5 exactly, so the nanos part
1293+ // never will be equal to 10^9.
1294+ ( 0 , nanos + add_ns as u32 )
12891295 } else if exp < $mant_bits {
12901296 let secs = u64 :: from( mant >> ( $mant_bits - exp) ) ;
12911297 let t = <$double_ty>:: from( ( mant << exp) & MANT_MASK ) ;
1298+ let nanos_offset = $mant_bits;
12921299 let nanos_tmp = <$double_ty>:: from( NANOS_PER_SEC ) * t;
1293- let nanos = ( nanos_tmp >> $mant_bits) as u32 ;
1294- if nanos_tmp & ( 1 << ( $mant_bits - 1 ) ) == 0 {
1295- ( secs, nanos)
1296- } else if nanos + 1 != NANOS_PER_SEC {
1297- ( secs, nanos + 1 )
1298- } else {
1299- // `secs + 1` can not overflow since `exp` is less than `$mant_bits`
1300- // and the latter is less than 64 bits for both `f32` and `f64`
1301- ( secs + 1 , 0 )
1302- }
1300+ let nanos = ( nanos_tmp >> nanos_offset) as u32 ;
1301+
1302+ let rem_mask = ( 1 << nanos_offset) - 1 ;
1303+ let rem_msb_mask = 1 << ( nanos_offset - 1 ) ;
1304+ let rem = nanos_tmp & rem_mask;
1305+ let is_tie = rem == rem_msb_mask;
1306+ let is_even = ( nanos & 1 ) == 0 ;
1307+ let rem_msb = nanos_tmp & rem_msb_mask == 0 ;
1308+ let add_ns = !( rem_msb || ( is_even && is_tie) ) ;
1309+
1310+ // neither `f32`, nor `f64` can represent x.999_999_999_5 exactly,
1311+ // so the nanos part never will be equal to 10^9
1312+ ( secs, nanos + add_ns as u32 )
13031313 } else if exp < 64 {
13041314 // the input has no fractional part
13051315 let secs = u64 :: from( mant) << ( exp - $mant_bits) ;
@@ -1348,6 +1358,28 @@ impl Duration {
13481358 /// assert!(res.is_err());
13491359 /// let res = Duration::try_from_secs_f32(2e19);
13501360 /// assert!(res.is_err());
1361+ ///
1362+ /// // this method uses round to nearest, ties to even
1363+ ///
1364+ /// // this float represents exactly 976562.5e-9
1365+ /// let val = f32::from_bits(0x3A80_0000);
1366+ /// let res = Duration::try_from_secs_f32(val);
1367+ /// assert_eq!(res, Ok(Duration::new(0, 976_562)));
1368+ ///
1369+ /// // this float represents exactly 2929687.5e-9
1370+ /// let val = f32::from_bits(0x3B40_0000);
1371+ /// let res = Duration::try_from_secs_f32(val);
1372+ /// assert_eq!(res, Ok(Duration::new(0, 2_929_688)));
1373+ ///
1374+ /// // this float represents exactly 1.000_976_562_5
1375+ /// let val = f32::from_bits(0x3F802000);
1376+ /// let res = Duration::try_from_secs_f32(val);
1377+ /// assert_eq!(res, Ok(Duration::new(1, 976_562)));
1378+ ///
1379+ /// // this float represents exactly 1.002_929_687_5
1380+ /// let val = f32::from_bits(0x3F806000);
1381+ /// let res = Duration::try_from_secs_f32(val);
1382+ /// assert_eq!(res, Ok(Duration::new(1, 2_929_688)));
13511383 /// ```
13521384 #[ unstable( feature = "duration_checked_float" , issue = "83400" ) ]
13531385 #[ inline]
@@ -1397,6 +1429,28 @@ impl Duration {
13971429 /// assert!(res.is_err());
13981430 /// let res = Duration::try_from_secs_f64(2e19);
13991431 /// assert!(res.is_err());
1432+ ///
1433+ /// // this method uses round to nearest, ties to even
1434+ ///
1435+ /// // this float represents exactly 976562.5e-9
1436+ /// let val = f64::from_bits(0x3F50_0000_0000_0000);
1437+ /// let res = Duration::try_from_secs_f64(val);
1438+ /// assert_eq!(res, Ok(Duration::new(0, 976_562)));
1439+ ///
1440+ /// // this float represents exactly 2929687.5e-9
1441+ /// let val = f64::from_bits(0x3F68_0000_0000_0000);
1442+ /// let res = Duration::try_from_secs_f64(val);
1443+ /// assert_eq!(res, Ok(Duration::new(0, 2_929_688)));
1444+ ///
1445+ /// // this float represents exactly 1.000_976_562_5
1446+ /// let val = f64::from_bits(0x3FF0_0400_0000_0000);
1447+ /// let res = Duration::try_from_secs_f64(val);
1448+ /// assert_eq!(res, Ok(Duration::new(1, 976_562)));
1449+ ///
1450+ /// // this float represents exactly 1.002_929_687_5
1451+ /// let val = f64::from_bits(0x3_FF00_C000_0000_000);
1452+ /// let res = Duration::try_from_secs_f64(val);
1453+ /// assert_eq!(res, Ok(Duration::new(1, 2_929_688)));
14001454 /// ```
14011455 #[ unstable( feature = "duration_checked_float" , issue = "83400" ) ]
14021456 #[ inline]
0 commit comments