@@ -51,6 +51,124 @@ impl fmt::Debug for Duration {
51
51
}
52
52
}
53
53
54
+ /// This is adapted from the `std` implementation, which uses mostly bit
55
+ /// operations to ensure the highest precision:
56
+ /// https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340
57
+ /// Changes from `std` are marked and explained below.
58
+ #[ rustfmt:: skip] // Skip `rustfmt` because it reformats the arguments of the macro weirdly.
59
+ macro_rules! try_from_secs {
60
+ (
61
+ secs = $secs: expr,
62
+ mantissa_bits = $mant_bits: literal,
63
+ exponent_bits = $exp_bits: literal,
64
+ offset = $offset: literal,
65
+ bits_ty = $bits_ty: ty,
66
+ bits_ty_signed = $bits_ty_signed: ty,
67
+ double_ty = $double_ty: ty,
68
+ float_ty = $float_ty: ty,
69
+ is_nan = $is_nan: expr,
70
+ is_overflow = $is_overflow: expr,
71
+ ) => { {
72
+ ' value: {
73
+ const MIN_EXP : i16 = 1 - ( 1i16 << $exp_bits) / 2 ;
74
+ const MANT_MASK : $bits_ty = ( 1 << $mant_bits) - 1 ;
75
+ const EXP_MASK : $bits_ty = ( 1 << $exp_bits) - 1 ;
76
+
77
+ // Change from std: No error check for negative values necessary.
78
+
79
+ let bits = $secs. to_bits( ) ;
80
+ let mant = ( bits & MANT_MASK ) | ( MANT_MASK + 1 ) ;
81
+ let exp = ( ( bits >> $mant_bits) & EXP_MASK ) as i16 + MIN_EXP ;
82
+
83
+ let ( secs, nanos) = if exp < -31 {
84
+ // the input represents less than 1ns and can not be rounded to it
85
+ ( 0u64 , 0u32 )
86
+ } else if exp < 0 {
87
+ // the input is less than 1 second
88
+ let t = <$double_ty>:: from( mant) << ( $offset + exp) ;
89
+ let nanos_offset = $mant_bits + $offset;
90
+ let nanos_tmp = u128 :: from( Nanosecond . per( Second ) ) * u128 :: from( t) ;
91
+ let nanos = ( nanos_tmp >> nanos_offset) as u32 ;
92
+
93
+ let rem_mask = ( 1 << nanos_offset) - 1 ;
94
+ let rem_msb_mask = 1 << ( nanos_offset - 1 ) ;
95
+ let rem = nanos_tmp & rem_mask;
96
+ let is_tie = rem == rem_msb_mask;
97
+ let is_even = ( nanos & 1 ) == 0 ;
98
+ let rem_msb = nanos_tmp & rem_msb_mask == 0 ;
99
+ let add_ns = !( rem_msb || ( is_even && is_tie) ) ;
100
+
101
+ // f32 does not have enough precision to trigger the second branch
102
+ // since it can not represent numbers between 0.999_999_940_395 and 1.0.
103
+ let nanos = nanos + add_ns as u32 ;
104
+ if ( $mant_bits == 23 ) || ( nanos != Nanosecond . per( Second ) ) {
105
+ ( 0 , nanos)
106
+ } else {
107
+ ( 1 , 0 )
108
+ }
109
+ } else if exp < $mant_bits {
110
+ let secs = u64 :: from( mant >> ( $mant_bits - exp) ) ;
111
+ let t = <$double_ty>:: from( ( mant << exp) & MANT_MASK ) ;
112
+ let nanos_offset = $mant_bits;
113
+ let nanos_tmp = <$double_ty>:: from( Nanosecond . per( Second ) ) * t;
114
+ let nanos = ( nanos_tmp >> nanos_offset) as u32 ;
115
+
116
+ let rem_mask = ( 1 << nanos_offset) - 1 ;
117
+ let rem_msb_mask = 1 << ( nanos_offset - 1 ) ;
118
+ let rem = nanos_tmp & rem_mask;
119
+ let is_tie = rem == rem_msb_mask;
120
+ let is_even = ( nanos & 1 ) == 0 ;
121
+ let rem_msb = nanos_tmp & rem_msb_mask == 0 ;
122
+ let add_ns = !( rem_msb || ( is_even && is_tie) ) ;
123
+
124
+ // f32 does not have enough precision to trigger the second branch.
125
+ // For example, it can not represent numbers between 1.999_999_880...
126
+ // and 2.0. Bigger values result in even smaller precision of the
127
+ // fractional part.
128
+ let nanos = nanos + add_ns as u32 ;
129
+ if ( $mant_bits == 23 ) || ( nanos != Nanosecond . per( Second ) ) {
130
+ ( secs, nanos)
131
+ } else {
132
+ ( secs + 1 , 0 )
133
+ }
134
+ } else if exp < 63 {
135
+ // Change from std: The exponent here is 63 instead of 64,
136
+ // because i64::MAX + 1 is 2^63.
137
+
138
+ // the input has no fractional part
139
+ let secs = u64 :: from( mant) << ( exp - $mant_bits) ;
140
+ ( secs, 0 )
141
+ } else if bits == ( i64 :: MIN as $float_ty) . to_bits( ) {
142
+ // Change from std: Signed integers are asymmetrical in that
143
+ // iN::MIN is -iN::MAX - 1. So for example i8 covers the
144
+ // following numbers -128..=127. The check above (exp < 63)
145
+ // doesn't cover i64::MIN as that is -2^63, so we have this
146
+ // additional case to handle the asymmetry of iN::MIN.
147
+ break ' value Self :: new_unchecked( i64 :: MIN , 0 ) ;
148
+ } else if $secs. is_nan( ) {
149
+ // Change from std: std doesn't differentiate between the error
150
+ // cases.
151
+ $is_nan
152
+ } else {
153
+ $is_overflow
154
+ } ;
155
+
156
+ // Change from std: All the code is mostly unmodified in that it
157
+ // simply calculates an unsigned integer. Here we extract the sign
158
+ // bit and assign it to the number. We basically manually do two's
159
+ // complement here, we could also use an if and just negate the
160
+ // numbers based on the sign, but it turns out to be quite a bit
161
+ // slower.
162
+ let mask = ( bits as $bits_ty_signed) >> ( $mant_bits + $exp_bits) ;
163
+ #[ allow( trivial_numeric_casts) ]
164
+ let secs_signed = ( ( secs as i64 ) ^ ( mask as i64 ) ) - ( mask as i64 ) ;
165
+ #[ allow( trivial_numeric_casts) ]
166
+ let nanos_signed = ( ( nanos as i32 ) ^ ( mask as i32 ) ) - ( mask as i32 ) ;
167
+ Self :: new_unchecked( secs_signed, nanos_signed)
168
+ }
169
+ } } ;
170
+ }
171
+
54
172
impl Duration {
55
173
// region: constants
56
174
/// Equivalent to `0.seconds()`.
@@ -321,17 +439,18 @@ impl Duration {
321
439
/// assert_eq!(Duration::seconds_f64(-0.5), -0.5.seconds());
322
440
/// ```
323
441
pub fn seconds_f64 ( seconds : f64 ) -> Self {
324
- if seconds > i64:: MAX as f64 || seconds < i64:: MIN as f64 {
325
- crate :: expect_failed ( "overflow constructing `time::Duration`" ) ;
326
- }
327
- if seconds. is_nan ( ) {
328
- crate :: expect_failed ( "passed NaN to `time::Duration::seconds_f64`" ) ;
329
- }
330
- let seconds_truncated = seconds as i64 ;
331
- // This only works because we handle the overflow condition above.
332
- let nanoseconds =
333
- ( ( seconds - seconds_truncated as f64 ) * Nanosecond . per ( Second ) as f64 ) as i32 ;
334
- Self :: new_unchecked ( seconds_truncated, nanoseconds)
442
+ try_from_secs ! (
443
+ secs = seconds,
444
+ mantissa_bits = 52 ,
445
+ exponent_bits = 11 ,
446
+ offset = 44 ,
447
+ bits_ty = u64 ,
448
+ bits_ty_signed = i64 ,
449
+ double_ty = u128 ,
450
+ float_ty = f64 ,
451
+ is_nan = crate :: expect_failed( "passed NaN to `time::Duration::seconds_f64`" ) ,
452
+ is_overflow = crate :: expect_failed( "overflow constructing `time::Duration`" ) ,
453
+ )
335
454
}
336
455
337
456
/// Creates a new `Duration` from the specified number of seconds represented as `f32`.
@@ -342,17 +461,154 @@ impl Duration {
342
461
/// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds());
343
462
/// ```
344
463
pub fn seconds_f32 ( seconds : f32 ) -> Self {
345
- if seconds > i64:: MAX as f32 || seconds < i64:: MIN as f32 {
346
- crate :: expect_failed ( "overflow constructing `time::Duration`" ) ;
347
- }
348
- if seconds. is_nan ( ) {
349
- crate :: expect_failed ( "passed NaN to `time::Duration::seconds_f32`" ) ;
350
- }
351
- let seconds_truncated = seconds as i64 ;
352
- // This only works because we handle the overflow condition above.
353
- let nanoseconds =
354
- ( ( seconds - seconds_truncated as f32 ) * Nanosecond . per ( Second ) as f32 ) as i32 ;
355
- Self :: new_unchecked ( seconds_truncated, nanoseconds)
464
+ try_from_secs ! (
465
+ secs = seconds,
466
+ mantissa_bits = 23 ,
467
+ exponent_bits = 8 ,
468
+ offset = 41 ,
469
+ bits_ty = u32 ,
470
+ bits_ty_signed = i32 ,
471
+ double_ty = u64 ,
472
+ float_ty = f32 ,
473
+ is_nan = crate :: expect_failed( "passed NaN to `time::Duration::seconds_f32`" ) ,
474
+ is_overflow = crate :: expect_failed( "overflow constructing `time::Duration`" ) ,
475
+ )
476
+ }
477
+
478
+ /// Creates a new `Duration` from the specified number of seconds
479
+ /// represented as `f64`. Any values that are out of bounds are saturated at
480
+ /// the minimum or maximum respectively. `NaN` gets turned into a `Duration`
481
+ /// of 0 seconds.
482
+ ///
483
+ /// ```rust
484
+ /// # use time::{Duration, ext::NumericalDuration};
485
+ /// assert_eq!(Duration::saturating_seconds_f64(0.5), 0.5.seconds());
486
+ /// assert_eq!(Duration::saturating_seconds_f64(-0.5), -0.5.seconds());
487
+ /// assert_eq!(
488
+ /// Duration::saturating_seconds_f64(f64::NAN),
489
+ /// Duration::new(0, 0),
490
+ /// );
491
+ /// assert_eq!(
492
+ /// Duration::saturating_seconds_f64(f64::NEG_INFINITY),
493
+ /// Duration::MIN,
494
+ /// );
495
+ /// assert_eq!(
496
+ /// Duration::saturating_seconds_f64(f64::INFINITY),
497
+ /// Duration::MAX,
498
+ /// );
499
+ /// ```
500
+ pub fn saturating_seconds_f64 ( seconds : f64 ) -> Self {
501
+ try_from_secs ! (
502
+ secs = seconds,
503
+ mantissa_bits = 52 ,
504
+ exponent_bits = 11 ,
505
+ offset = 44 ,
506
+ bits_ty = u64 ,
507
+ bits_ty_signed = i64 ,
508
+ double_ty = u128 ,
509
+ float_ty = f64 ,
510
+ is_nan = ( 0 , 0 ) ,
511
+ is_overflow = return if seconds < 0.0 {
512
+ Self :: new_unchecked( i64 :: MIN , -999_999_999 )
513
+ } else {
514
+ Self :: new_unchecked( i64 :: MAX , 999_999_999 )
515
+ } ,
516
+ )
517
+ }
518
+
519
+ /// Creates a new `Duration` from the specified number of seconds
520
+ /// represented as `f32`. Any values that are out of bounds are saturated at
521
+ /// the minimum or maximum respectively. `NaN` gets turned into a `Duration`
522
+ /// of 0 seconds.
523
+ ///
524
+ /// ```rust
525
+ /// # use time::{Duration, ext::NumericalDuration};
526
+ /// assert_eq!(Duration::saturating_seconds_f32(0.5), 0.5.seconds());
527
+ /// assert_eq!(Duration::saturating_seconds_f32(-0.5), (-0.5).seconds());
528
+ /// assert_eq!(
529
+ /// Duration::saturating_seconds_f32(f32::NAN),
530
+ /// Duration::new(0, 0),
531
+ /// );
532
+ /// assert_eq!(
533
+ /// Duration::saturating_seconds_f32(f32::NEG_INFINITY),
534
+ /// Duration::MIN,
535
+ /// );
536
+ /// assert_eq!(
537
+ /// Duration::saturating_seconds_f32(f32::INFINITY),
538
+ /// Duration::MAX,
539
+ /// );
540
+ /// ```
541
+ pub fn saturating_seconds_f32 ( seconds : f32 ) -> Self {
542
+ try_from_secs ! (
543
+ secs = seconds,
544
+ mantissa_bits = 23 ,
545
+ exponent_bits = 8 ,
546
+ offset = 41 ,
547
+ bits_ty = u32 ,
548
+ bits_ty_signed = i32 ,
549
+ double_ty = u64 ,
550
+ float_ty = f32 ,
551
+ is_nan = ( 0 , 0 ) ,
552
+ is_overflow = return if seconds < 0.0 {
553
+ Self :: new_unchecked( i64 :: MIN , -999_999_999 )
554
+ } else {
555
+ Self :: new_unchecked( i64 :: MAX , 999_999_999 )
556
+ } ,
557
+ )
558
+ }
559
+
560
+ /// Creates a new `Duration` from the specified number of seconds
561
+ /// represented as `f64`. Returns `None` if the `Duration` can't be
562
+ /// represented.
563
+ ///
564
+ /// ```rust
565
+ /// # use time::{Duration, ext::NumericalDuration};
566
+ /// assert_eq!(Duration::checked_seconds_f64(0.5), Some(0.5.seconds()));
567
+ /// assert_eq!(Duration::checked_seconds_f64(-0.5), Some(-0.5.seconds()));
568
+ /// assert_eq!(Duration::checked_seconds_f64(f64::NAN), None);
569
+ /// assert_eq!(Duration::checked_seconds_f64(f64::NEG_INFINITY), None);
570
+ /// assert_eq!(Duration::checked_seconds_f64(f64::INFINITY), None);
571
+ /// ```
572
+ pub fn checked_seconds_f64 ( seconds : f64 ) -> Option < Self > {
573
+ Some ( try_from_secs ! (
574
+ secs = seconds,
575
+ mantissa_bits = 52 ,
576
+ exponent_bits = 11 ,
577
+ offset = 44 ,
578
+ bits_ty = u64 ,
579
+ bits_ty_signed = i64 ,
580
+ double_ty = u128 ,
581
+ float_ty = f64 ,
582
+ is_nan = return None ,
583
+ is_overflow = return None ,
584
+ ) )
585
+ }
586
+
587
+ /// Creates a new `Duration` from the specified number of seconds
588
+ /// represented as `f32`. Returns `None` if the `Duration` can't be
589
+ /// represented.
590
+ ///
591
+ /// ```rust
592
+ /// # use time::{Duration, ext::NumericalDuration};
593
+ /// assert_eq!(Duration::checked_seconds_f32(0.5), Some(0.5.seconds()));
594
+ /// assert_eq!(Duration::checked_seconds_f32(-0.5), Some(-0.5.seconds()));
595
+ /// assert_eq!(Duration::checked_seconds_f32(f32::NAN), None);
596
+ /// assert_eq!(Duration::checked_seconds_f32(f32::NEG_INFINITY), None);
597
+ /// assert_eq!(Duration::checked_seconds_f32(f32::INFINITY), None);
598
+ /// ```
599
+ pub fn checked_seconds_f32 ( seconds : f32 ) -> Option < Self > {
600
+ Some ( try_from_secs ! (
601
+ secs = seconds,
602
+ mantissa_bits = 23 ,
603
+ exponent_bits = 8 ,
604
+ offset = 41 ,
605
+ bits_ty = u32 ,
606
+ bits_ty_signed = i32 ,
607
+ double_ty = u64 ,
608
+ float_ty = f32 ,
609
+ is_nan = return None ,
610
+ is_overflow = return None ,
611
+ ) )
356
612
}
357
613
358
614
/// Create a new `Duration` with the given number of milliseconds.
0 commit comments