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