Skip to content

Commit c2d8445

Browse files
committed
implement tie to even
1 parent 65c75b2 commit c2d8445

File tree

1 file changed

+73
-19
lines changed

1 file changed

+73
-19
lines changed

library/core/src/time.rs

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)