@@ -1270,36 +1270,46 @@ macro_rules! try_from_secs {
1270
1270
let mant = ( bits & MANT_MASK ) | ( MANT_MASK + 1 ) ;
1271
1271
let exp = ( ( bits >> $mant_bits) & EXP_MASK ) as i16 + MIN_EXP ;
1272
1272
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
1275
1275
( 0u64 , 0u32 )
1276
1276
} else if exp < 0 {
1277
1277
// the input is less than 1 second
1278
1278
let t = <$double_ty>:: from( mant) << ( $offset + exp) ;
1279
1279
let nanos_offset = $mant_bits + $offset;
1280
1280
let nanos_tmp = u128 :: from( NANOS_PER_SEC ) * u128 :: from( t) ;
1281
1281
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 )
1289
1295
} else if exp < $mant_bits {
1290
1296
let secs = u64 :: from( mant >> ( $mant_bits - exp) ) ;
1291
1297
let t = <$double_ty>:: from( ( mant << exp) & MANT_MASK ) ;
1298
+ let nanos_offset = $mant_bits;
1292
1299
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 )
1303
1313
} else if exp < 64 {
1304
1314
// the input has no fractional part
1305
1315
let secs = u64 :: from( mant) << ( exp - $mant_bits) ;
@@ -1348,6 +1358,28 @@ impl Duration {
1348
1358
/// assert!(res.is_err());
1349
1359
/// let res = Duration::try_from_secs_f32(2e19);
1350
1360
/// 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)));
1351
1383
/// ```
1352
1384
#[ unstable( feature = "duration_checked_float" , issue = "83400" ) ]
1353
1385
#[ inline]
@@ -1397,6 +1429,28 @@ impl Duration {
1397
1429
/// assert!(res.is_err());
1398
1430
/// let res = Duration::try_from_secs_f64(2e19);
1399
1431
/// 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)));
1400
1454
/// ```
1401
1455
#[ unstable( feature = "duration_checked_float" , issue = "83400" ) ]
1402
1456
#[ inline]
0 commit comments