Skip to content

Commit 65c75b2

Browse files
committed
Use rounding in float to Duration conversion methods
1 parent 563250a commit 65c75b2

File tree

1 file changed

+38
-23
lines changed

1 file changed

+38
-23
lines changed

library/core/src/time.rs

+38-23
Original file line numberDiff line numberDiff line change
@@ -730,9 +730,9 @@ impl Duration {
730730
/// // subnormal float
731731
/// let res = Duration::from_secs_f64(f64::from_bits(1));
732732
/// assert_eq!(res, Duration::new(0, 0));
733-
/// // conversion uses truncation, not rounding
733+
/// // conversion uses rounding
734734
/// let res = Duration::from_secs_f64(0.999e-9);
735-
/// assert_eq!(res, Duration::new(0, 0));
735+
/// assert_eq!(res, Duration::new(0, 1));
736736
/// ```
737737
#[stable(feature = "duration_float", since = "1.38.0")]
738738
#[must_use]
@@ -760,17 +760,17 @@ impl Duration {
760760
/// let res = Duration::from_secs_f32(1e-20);
761761
/// assert_eq!(res, Duration::new(0, 0));
762762
/// let res = Duration::from_secs_f32(4.2e-7);
763-
/// assert_eq!(res, Duration::new(0, 419));
763+
/// assert_eq!(res, Duration::new(0, 420));
764764
/// let res = Duration::from_secs_f32(2.7);
765-
/// assert_eq!(res, Duration::new(2, 700_000_047));
765+
/// assert_eq!(res, Duration::new(2, 700_000_048));
766766
/// let res = Duration::from_secs_f32(3e10);
767767
/// assert_eq!(res, Duration::new(30_000_001_024, 0));
768768
/// // subnormal float
769769
/// let res = Duration::from_secs_f32(f32::from_bits(1));
770770
/// assert_eq!(res, Duration::new(0, 0));
771-
/// // conversion uses truncation, not rounding
771+
/// // conversion uses rounding
772772
/// let res = Duration::from_secs_f32(0.999e-9);
773-
/// assert_eq!(res, Duration::new(0, 0));
773+
/// assert_eq!(res, Duration::new(0, 1));
774774
/// ```
775775
#[stable(feature = "duration_float", since = "1.38.0")]
776776
#[must_use]
@@ -815,7 +815,7 @@ impl Duration {
815815
/// use std::time::Duration;
816816
///
817817
/// let dur = Duration::new(2, 700_000_000);
818-
/// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_640));
818+
/// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_641));
819819
/// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847800, 0));
820820
/// ```
821821
#[stable(feature = "duration_float", since = "1.38.0")]
@@ -838,8 +838,7 @@ impl Duration {
838838
///
839839
/// let dur = Duration::new(2, 700_000_000);
840840
/// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611));
841-
/// // note that truncation is used, not rounding
842-
/// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_598));
841+
/// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_599));
843842
/// ```
844843
#[stable(feature = "duration_float", since = "1.38.0")]
845844
#[must_use = "this returns the result of the operation, \
@@ -862,9 +861,8 @@ impl Duration {
862861
/// let dur = Duration::new(2, 700_000_000);
863862
/// // note that due to rounding errors result is slightly
864863
/// // different from 0.859_872_611
865-
/// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_579));
866-
/// // note that truncation is used, not rounding
867-
/// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_598));
864+
/// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_580));
865+
/// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_599));
868866
/// ```
869867
#[stable(feature = "duration_float", since = "1.38.0")]
870868
#[must_use = "this returns the result of the operation, \
@@ -1278,13 +1276,30 @@ macro_rules! try_from_secs {
12781276
} else if exp < 0 {
12791277
// the input is less than 1 second
12801278
let t = <$double_ty>::from(mant) << ($offset + exp);
1281-
let nanos = (u128::from(NANOS_PER_SEC) * u128::from(t)) >> ($mant_bits + $offset);
1282-
(0, nanos as u32)
1279+
let nanos_offset = $mant_bits + $offset;
1280+
let nanos_tmp = u128::from(NANOS_PER_SEC) * u128::from(t);
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+
}
12831289
} else if exp < $mant_bits {
1284-
let secs = mant >> ($mant_bits - exp);
1290+
let secs = u64::from(mant >> ($mant_bits - exp));
12851291
let t = <$double_ty>::from((mant << exp) & MANT_MASK);
1286-
let nanos = (<$double_ty>::from(NANOS_PER_SEC) * t) >> $mant_bits;
1287-
(u64::from(secs), nanos as u32)
1292+
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+
}
12881303
} else if exp < 64 {
12891304
// the input has no fractional part
12901305
let secs = u64::from(mant) << (exp - $mant_bits);
@@ -1315,17 +1330,17 @@ impl Duration {
13151330
/// let res = Duration::try_from_secs_f32(1e-20);
13161331
/// assert_eq!(res, Ok(Duration::new(0, 0)));
13171332
/// let res = Duration::try_from_secs_f32(4.2e-7);
1318-
/// assert_eq!(res, Ok(Duration::new(0, 419)));
1333+
/// assert_eq!(res, Ok(Duration::new(0, 420)));
13191334
/// let res = Duration::try_from_secs_f32(2.7);
1320-
/// assert_eq!(res, Ok(Duration::new(2, 700_000_047)));
1335+
/// assert_eq!(res, Ok(Duration::new(2, 700_000_048)));
13211336
/// let res = Duration::try_from_secs_f32(3e10);
13221337
/// assert_eq!(res, Ok(Duration::new(30_000_001_024, 0)));
13231338
/// // subnormal float:
13241339
/// let res = Duration::try_from_secs_f32(f32::from_bits(1));
13251340
/// assert_eq!(res, Ok(Duration::new(0, 0)));
1326-
/// // conversion uses truncation, not rounding
1341+
/// // conversion uses rounding
13271342
/// let res = Duration::try_from_secs_f32(0.999e-9);
1328-
/// assert_eq!(res, Ok(Duration::new(0, 0)));
1343+
/// assert_eq!(res, Ok(Duration::new(0, 1)));
13291344
///
13301345
/// let res = Duration::try_from_secs_f32(-5.0);
13311346
/// assert!(res.is_err());
@@ -1372,9 +1387,9 @@ impl Duration {
13721387
/// // subnormal float
13731388
/// let res = Duration::try_from_secs_f64(f64::from_bits(1));
13741389
/// assert_eq!(res, Ok(Duration::new(0, 0)));
1375-
/// // conversion uses truncation, not rounding
1390+
/// // conversion uses rounding
13761391
/// let res = Duration::try_from_secs_f32(0.999e-9);
1377-
/// assert_eq!(res, Ok(Duration::new(0, 0)));
1392+
/// assert_eq!(res, Ok(Duration::new(0, 1)));
13781393
///
13791394
/// let res = Duration::try_from_secs_f64(-5.0);
13801395
/// assert!(res.is_err());

0 commit comments

Comments
 (0)