1use crate::tick::Tick;
5use core::cmp::Ordering;
6use core::fmt::{Debug, Formatter};
7use core::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
8
9use bevy_reflect::Reflect;
10use core::time::Duration;
11use fixed::traits::ToFixed;
12use fixed::types::{I32F32, U0F8, U0F16, U32F32};
13use lightyear_serde::reader::ReadInteger;
14use lightyear_serde::reader::Reader;
15use lightyear_serde::writer::WriteInteger;
16use lightyear_serde::{SerializationError, ToBytes};
17use serde::{Deserialize, Serialize};
18
19#[cfg(any(not(feature = "test_utils"), feature = "not_mock"))]
20pub use bevy_platform::time::Instant;
21#[cfg(all(feature = "test_utils", not(feature = "not_mock")))]
23pub use mock_instant::global::Instant;
24
25#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default, Reflect)]
32#[reflect(opaque)]
33pub struct Overstep {
34 value: U0F16,
35}
36
37impl Overstep {
38 pub fn new(value: U0F16) -> Self {
39 Self { value }
40 }
41 pub const fn lit(src: &str) -> Self {
42 Self {
43 value: U0F16::lit(src),
44 }
45 }
46
47 pub fn value(&self) -> U0F16 {
48 self.value
49 }
50
51 pub fn from_f32(value: f32) -> Self {
52 Self::new(U0F16::saturating_from_num(value))
53 }
54
55 pub fn to_f32(&self) -> f32 {
56 self.value.into()
57 }
58
59 pub fn from_u8(value: u8) -> Self {
60 Self::new(U0F8::from_bits(value).into())
61 }
62
63 pub fn to_u8(&self) -> u8 {
64 self.value.to_num::<U0F8>().to_bits()
65 }
66}
67
68impl PartialEq for Overstep {
69 fn eq(&self, other: &Self) -> bool {
70 self.to_u8() == other.to_u8()
72 }
73}
74
75impl Eq for Overstep {}
76
77impl PartialOrd for Overstep {
78 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
79 Some(self.cmp(other))
80 }
81}
82
83impl Ord for Overstep {
84 fn cmp(&self, other: &Self) -> Ordering {
85 self.value
86 .partial_cmp(&other.value)
87 .expect("NaN overstep is invalid")
88 }
89}
90
91impl ToBytes for Overstep {
92 fn bytes_len(&self) -> usize {
93 1 }
95
96 fn to_bytes(&self, buffer: &mut impl WriteInteger) -> Result<(), SerializationError> {
97 Ok(buffer.write_u8(self.to_u8())?)
98 }
99
100 fn from_bytes(buffer: &mut lightyear_serde::reader::Reader) -> Result<Self, SerializationError>
101 where
102 Self: Sized,
103 {
104 Ok(Self::from_u8(buffer.read_u8()?))
105 }
106}
107
108impl Add for Overstep {
109 type Output = Self;
110
111 fn add(self, rhs: Self) -> Self::Output {
112 Self::new(self.value + rhs.value)
113 }
114}
115
116impl Sub for Overstep {
117 type Output = Self;
118
119 fn sub(self, rhs: Self) -> Self::Output {
120 Self::new(self.value - rhs.value)
121 }
122}
123
124impl AddAssign for Overstep {
125 fn add_assign(&mut self, rhs: Self) {
126 self.value = self.value.saturating_add(rhs.value);
127 }
128}
129
130impl SubAssign for Overstep {
131 fn sub_assign(&mut self, rhs: Self) {
132 self.value = self.value.saturating_sub(rhs.value);
133 }
134}
135
136impl From<f32> for Overstep {
137 fn from(value: f32) -> Self {
138 Self::from_f32(value)
139 }
140}
141
142impl From<Overstep> for f32 {
143 fn from(overstep: Overstep) -> Self {
144 overstep.to_f32()
145 }
146}
147
148#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Reflect)]
157#[reflect(opaque)]
158pub struct TickInstant {
159 pub value: U32F32,
160}
161
162impl Debug for TickInstant {
163 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
164 write!(f, "{}", self.value)
165 }
166}
167
168impl TickInstant {
169 pub const fn lit(src: &str) -> Self {
170 Self {
171 value: U32F32::lit(src),
172 }
173 }
174 pub const fn zero() -> Self {
175 Self {
176 value: U32F32::ZERO,
177 }
178 }
179 pub fn tick(&self) -> Tick {
180 Tick(self.value.to_num())
181 }
182 pub fn overstep(&self) -> Overstep {
184 Overstep::new(self.value.wrapping_to_fixed())
185 }
186
187 pub fn from_tick_and_overstep(tick: Tick, overstep: Overstep) -> Self {
192 let base: U32F32 = tick.0.to_fixed();
193 let frac: U32F32 = overstep.value().into();
194 Self { value: base + frac }
195 }
196
197 pub fn as_duration(&self, tick_duration: Duration) -> Duration {
199 tick_duration.mul_f32(self.value.to_num())
200 }
201
202 pub fn as_time_delta(&self, tick_duration: Duration) -> TimeDelta {
203 let duration = self.as_duration(tick_duration);
204 TimeDelta::from_duration(duration).expect("Duration should be valid")
205 }
206
207 pub fn from_duration(duration: Duration, tick_duration: Duration) -> Self {
209 let ticks_f32 = duration.as_secs_f32() / tick_duration.as_secs_f32();
210 Self {
211 value: ticks_f32.wrapping_to_fixed(),
212 }
213 }
214
215 pub fn from_time_delta(delta: TimeDelta, tick_duration: Duration) -> Self {
216 let duration = delta.as_duration().expect("Duration should be valid");
217 Self::from_duration(duration, tick_duration)
218 }
219}
220
221impl From<TickDelta> for TickInstant {
222 fn from(value: TickDelta) -> Self {
223 if value.is_negative() {
224 panic!("Cannot convert negative TickDelta to TickInstant");
225 }
226 Self {
227 value: value.value.cast_unsigned(),
228 }
229 }
230}
231
232impl From<Tick> for TickInstant {
233 fn from(value: Tick) -> Self {
234 Self {
235 value: value.0.to_fixed(),
236 }
237 }
238}
239
240#[derive(Clone, Copy, PartialEq, Eq, Reflect)]
242#[reflect(opaque)]
243pub struct TickDelta {
244 value: I32F32,
247}
248
249impl Debug for TickDelta {
250 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
251 write!(f, "{:.6}", self.value)
252 }
253}
254
255impl From<Tick> for TickDelta {
256 fn from(value: Tick) -> Self {
257 Self {
258 value: value.0.cast_signed().to_fixed(),
259 }
260 }
261}
262
263impl From<i32> for TickDelta {
264 fn from(value: i32) -> Self {
265 Self {
266 value: value.to_fixed(),
267 }
268 }
269}
270
271impl From<PositiveTickDelta> for TickDelta {
272 fn from(value: PositiveTickDelta) -> Self {
273 Self {
274 value: value.value.wrapping_to_fixed(),
275 }
276 }
277}
278
279impl From<TickInstant> for TickDelta {
280 fn from(value: TickInstant) -> Self {
281 Self {
282 value: value.value.cast_signed(),
283 }
284 }
285}
286
287impl TickDelta {
288 pub fn new(value: I32F32) -> Self {
289 Self { value }
290 }
291 pub const fn lit(src: &str) -> Self {
292 Self {
293 value: I32F32::lit(src),
294 }
295 }
296
297 pub fn tick_diff(&self) -> u32 {
298 self.value.unsigned_abs().to_num::<u32>()
299 }
300 pub fn overstep(&self) -> Overstep {
301 Overstep::new(self.value.unsigned_abs().wrapping_to_num())
302 }
303
304 pub fn is_positive(&self) -> bool {
305 self.value.is_positive()
306 }
307
308 pub fn is_negative(&self) -> bool {
309 self.value.is_negative()
310 }
311
312 pub fn to_duration(&self, tick_duration: Duration) -> Duration {
313 tick_duration.mul_f32(self.value.to_num())
314 }
315
316 pub fn from_duration(duration: Duration, tick_duration: Duration) -> Self {
317 debug_assert!(
318 tick_duration > Duration::ZERO,
319 "Tick duration must be positive"
320 );
321 let ticks_f32 = duration.as_secs_f32() / tick_duration.as_secs_f32();
322 Self {
323 value: ticks_f32.wrapping_to_fixed(),
324 }
325 }
326
327 pub fn to_time_delta(&self, tick_duration: Duration) -> TimeDelta {
328 let tick_duration_f32 = tick_duration.as_secs_f32();
329 let duration = tick_duration_f32 * self.value.to_num::<f32>();
330 if self.is_negative() {
331 match TimeDelta::from_duration(Duration::from_secs_f32(-duration)) {
333 Ok(delta) => -delta,
334 Err(_) => panic!("Failed to convert duration to TimeDelta"),
335 }
336 } else {
337 TimeDelta::from_duration(Duration::from_secs_f32(duration))
338 .expect("Duration should be valid")
339 }
340 }
341
342 pub fn to_f32(&self) -> f32 {
343 self.value.to_num()
344 }
345
346 pub fn from_i32(delta: i32) -> Self {
348 Self {
349 value: delta.to_fixed(),
350 }
351 }
352
353 pub fn to_i32(&self) -> i32 {
356 self.value.to_num()
357 }
358
359 pub fn from_time_delta(mut delta: TimeDelta, tick_duration: Duration) -> Self {
360 let is_negative = !delta.is_positive();
361 if is_negative {
362 delta = -delta;
363 }
364
365 let duration = match delta.as_duration() {
367 Ok(d) => d,
368 Err(_) => panic!("Failed to convert TimeDelta to Duration"),
369 };
370
371 let mut ticks_f32 = duration.as_secs_f32() / tick_duration.as_secs_f32();
372 if is_negative {
373 ticks_f32 = -ticks_f32;
374 }
375 Self {
376 value: ticks_f32.wrapping_to_fixed(),
377 }
378 }
379
380 pub fn zero() -> Self {
381 Self {
382 value: I32F32::default(),
383 }
384 }
385}
386
387impl Neg for TickDelta {
388 type Output = Self;
389
390 fn neg(self) -> Self::Output {
391 Self { value: -self.value }
392 }
393}
394
395impl Add for TickDelta {
396 type Output = TickDelta;
397
398 fn add(self, rhs: Self) -> Self::Output {
399 Self {
400 value: self.value.wrapping_add(rhs.value),
401 }
402 }
403}
404
405impl Sub for TickDelta {
406 type Output = TickDelta;
407
408 fn sub(self, rhs: Self) -> Self::Output {
409 Self {
410 value: self.value.wrapping_sub(rhs.value),
411 }
412 }
413}
414
415impl Mul<f32> for TickDelta {
416 type Output = Self;
417
418 fn mul(self, rhs: f32) -> Self::Output {
419 Self {
420 value: self.value.to_num::<f32>().mul(rhs).wrapping_to_fixed(),
421 }
422 }
423}
424
425impl Mul<U0F16> for TickDelta {
426 type Output = Self;
427
428 fn mul(self, rhs: U0F16) -> Self::Output {
429 let rhs_fixed: I32F32 = rhs.to_fixed::<I32F32>();
430 Self {
431 value: self.value.wrapping_mul(rhs_fixed),
432 }
433 }
434}
435
436#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default, Reflect)]
437#[reflect(opaque)]
438pub struct PositiveTickDelta {
439 pub value: U32F32,
440}
441
442impl PositiveTickDelta {
443 pub const fn lit(src: &str) -> Self {
444 Self {
445 value: U32F32::lit(src),
446 }
447 }
448 pub fn tick_diff(&self) -> u32 {
449 self.value.to_num::<u32>()
450 }
451 pub fn overstep(&self) -> Overstep {
452 Overstep::new(self.value.wrapping_to_num())
453 }
454}
455
456impl From<TickDelta> for PositiveTickDelta {
457 fn from(value: TickDelta) -> Self {
458 if value.is_negative() {
459 panic!("Cannot convert negative TickDelta to PositiveTickDelta");
460 }
461 Self {
462 value: value.value.cast_unsigned(),
463 }
464 }
465}
466
467impl ToBytes for PositiveTickDelta {
468 fn bytes_len(&self) -> usize {
469 4 + self.overstep().bytes_len()
470 }
471
472 fn to_bytes(&self, buffer: &mut impl WriteInteger) -> Result<(), SerializationError> {
474 buffer.write_u32(self.tick_diff())?;
475 self.overstep().to_bytes(buffer)
476 }
477
478 fn from_bytes(buffer: &mut Reader) -> Result<Self, SerializationError>
479 where
480 Self: Sized,
481 {
482 let tick_diff = buffer.read_u32()?;
483 let overstep = Overstep::from_bytes(buffer)?;
484 Ok(Self {
485 value: tick_diff.to_fixed::<U32F32>() + U32F32::from(overstep.value),
486 })
487 }
488}
489
490#[derive(Debug, PartialEq, Eq, Clone, Copy)]
494pub struct TimeDelta {
495 duration: chrono::TimeDelta,
496}
497
498impl TimeDelta {
499 pub fn is_positive(&self) -> bool {
500 self.duration.num_nanoseconds().unwrap_or(0) >= 0
501 }
502
503 pub fn as_duration(&self) -> Result<Duration, chrono::OutOfRangeError> {
505 self.duration.to_std()
506 }
507
508 pub fn from_duration(duration: Duration) -> Result<Self, chrono::OutOfRangeError> {
509 Ok(Self {
510 duration: chrono::TimeDelta::from_std(duration)?,
511 })
512 }
513}
514
515impl Neg for TimeDelta {
516 type Output = Self;
517
518 fn neg(self) -> Self::Output {
519 Self {
520 duration: -self.duration,
521 }
522 }
523}
524
525impl Add<TickDelta> for TickInstant {
526 type Output = TickInstant;
527
528 fn add(self, rhs: TickDelta) -> Self::Output {
529 TickInstant {
530 value: self.value.wrapping_add_signed(rhs.value),
531 }
532 }
533}
534
535impl Sub<TickDelta> for TickInstant {
536 type Output = TickInstant;
537
538 fn sub(self, rhs: TickDelta) -> Self::Output {
539 TickInstant {
540 value: self.value.wrapping_sub_signed(rhs.value),
541 }
542 }
543}
544
545impl Sub for TickInstant {
546 type Output = TickDelta;
547
548 fn sub(self, rhs: TickInstant) -> Self::Output {
549 TickDelta {
550 value: self.value.cast_signed().wrapping_sub_unsigned(rhs.value),
551 }
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 use approx::{AbsDiffEq, assert_abs_diff_eq, assert_relative_eq};
559 use core::time::Duration;
560
561 impl AbsDiffEq for Overstep {
562 type Epsilon = Overstep;
563
564 fn default_epsilon() -> Self::Epsilon {
565 Overstep {
566 value: U0F16::DELTA,
567 }
568 }
569
570 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
571 self.value.abs_diff(other.value) <= epsilon.value
572 }
573 }
574
575 impl AbsDiffEq for TickInstant {
576 type Epsilon = TickInstant;
577
578 fn default_epsilon() -> Self::Epsilon {
579 TickInstant {
580 value: U32F32::DELTA,
581 }
582 }
583
584 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
585 self.value.abs_diff(other.value) <= epsilon.value
586 }
587 }
588
589 impl AbsDiffEq for TickDelta {
590 type Epsilon = TickDelta;
591
592 fn default_epsilon() -> Self::Epsilon {
593 TickDelta {
594 value: I32F32::DELTA,
595 }
596 }
597
598 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
599 self.value.abs_diff(other.value) <= epsilon.value
600 }
601 }
602
603 #[test]
604 fn test_overstep_quantization_error() {
605 for i in 0..=10 {
607 let original_value = i as f32 / 10.0;
608 let overstep = Overstep::from_f32(original_value);
609 let quantized = overstep.to_u8();
610 let round_trip = Overstep::from_u8(quantized).to_f32();
611
612 assert_relative_eq!(round_trip, original_value, epsilon = 0.01);
613 }
614 }
615
616 #[test]
617 fn test_tickinstant_ordering() {
618 let t1 = TickInstant::lit("10.5");
619 let t2 = TickInstant::lit("10.7");
620 let t3 = TickInstant::lit("11.2");
621
622 assert!(t1 < t2);
623 assert!(t2 < t3);
624 assert!(t1 < t3);
625
626 assert_eq!(t1.cmp(&t1), Ordering::Equal);
627 assert_eq!(t1.cmp(&t2), Ordering::Less);
628 assert_eq!(t2.cmp(&t1), Ordering::Greater);
629 }
630
631 #[test]
632 fn test_tickinstant_add_positive_tickdelta() {
633 let tick_instant = TickInstant::lit("10.3");
634 let tick_delta = TickDelta::lit("5.2");
635
636 let result = tick_instant + tick_delta;
637
638 assert_abs_diff_eq!(result, TickInstant::lit("15.5"));
639 }
640
641 #[test]
642 fn test_tickinstant_add_negative_tickdelta() {
643 let tick_instant = TickInstant::lit("10.3");
644 let tick_delta = TickDelta::lit("-5.2"); let result = tick_instant + tick_delta;
647
648 assert_abs_diff_eq!(result, TickInstant::lit("5.1"));
649 }
650
651 #[test]
652 fn test_tickinstant_add_with_overstep_overflow() {
653 let tick_instant = TickInstant::lit("10.7");
654 let tick_delta = TickDelta::lit("5.6");
655
656 let result = tick_instant + tick_delta;
657
658 assert_abs_diff_eq!(result, TickInstant::lit("16.3"));
660 }
661
662 #[test]
663 fn test_tickinstant_sub_positive_tickdelta() {
664 let tick_instant = TickInstant::lit("10.7");
665 let tick_delta = TickDelta::lit("5.2");
666
667 let result = tick_instant - tick_delta;
668
669 assert_abs_diff_eq!(result, TickInstant::lit("5.5"));
670 }
671
672 #[test]
673 fn test_tickinstant_sub_negative_tickdelta() {
674 let tick_instant = TickInstant::lit("10.3");
675 let tick_delta = TickDelta::lit("-5.2"); let result = tick_instant - tick_delta;
678
679 assert_abs_diff_eq!(result, TickInstant::lit("15.5"));
680 }
681
682 #[test]
683 fn test_tickinstant_sub_with_overstep_underflow() {
684 let tick_instant = TickInstant::lit("10.3");
685 let tick_delta = TickDelta::lit("5.7");
686
687 let result = tick_instant - tick_delta;
688
689 assert_abs_diff_eq!(result, TickInstant::lit("4.6"));
691 }
692
693 #[test]
694 fn test_tickinstant_sub_tickinstant() {
695 let t1 = TickInstant::lit("15.7");
696 let t2 = TickInstant::lit("10.3");
697
698 let delta = t1 - t2;
700 assert_abs_diff_eq!(delta, TickDelta::lit("5.4"));
701
702 let delta = t2 - t1;
704 assert_abs_diff_eq!(delta, TickDelta::lit("-5.4"));
705 }
706
707 #[test]
708 fn test_tickinstant_sub_tickinstant_with_overstep_underflow() {
709 let t1 = TickInstant::lit("15.2");
710 let t2 = TickInstant::lit("10.7");
711
712 let delta = t1 - t2;
714 assert_abs_diff_eq!(delta, TickDelta::lit("4.5"));
715 }
716
717 #[test]
718 fn test_tickdelta_accessors() {
719 let delta = TickDelta::lit("-32768");
720 assert_eq!(delta.is_positive(), false);
721 assert_eq!(delta.is_negative(), true);
722 assert_eq!(delta.tick_diff(), 32768);
723 assert_eq!(delta.overstep().to_f32(), 0.0);
724
725 let delta = TickDelta::lit("-32767.75");
726 assert_eq!(delta.is_positive(), false);
727 assert_eq!(delta.is_negative(), true);
728 assert_eq!(delta.tick_diff(), 32767);
729 assert_eq!(delta.overstep().to_f32(), 0.75);
730
731 let delta = TickDelta::lit("32767.75");
732 assert_eq!(delta.is_positive(), true);
733 assert_eq!(delta.is_negative(), false);
734 assert_eq!(delta.tick_diff(), 32767);
735 assert_eq!(delta.overstep().to_f32(), 0.75);
736 }
737
738 #[test]
739 fn test_tickdelta_negation() {
740 let delta = TickDelta::lit("5.3");
741 let negated = -delta;
742
743 assert_abs_diff_eq!(negated, TickDelta::lit("-5.3"));
744
745 let double_negated = -negated;
747
748 assert_abs_diff_eq!(double_negated, TickDelta::lit("5.3"));
749 }
750
751 #[test]
752 fn test_tickdelta_signed_addition() {
753 let delta = TickDelta::from_i32(10);
754
755 assert_eq!((delta + delta).to_i32(), 20);
756 assert_eq!((delta + (-delta)).to_i32(), 0);
757 assert_eq!(((-delta) + delta).to_i32(), 0);
758 assert_eq!(((-delta) + (-delta)).to_i32(), -20);
759 }
760
761 #[test]
762 fn test_tickdelta_multiplication() {
763 let delta = TickDelta::lit("10.5");
764
765 let result = delta * 2.0;
767 assert_eq!(result, TickDelta::lit("21"));
768 assert_relative_eq!(result.overstep().to_f32(), 0.0);
769
770 let result = delta * 1.5;
772 assert_eq!(result, TickDelta::lit("15.75"));
773
774 let delta = TickDelta::lit("10.8");
776 let result = delta * 1.5;
777 assert_abs_diff_eq!(
778 result,
779 TickDelta::lit("16.2"),
780 epsilon = TickDelta::lit("0.001")
781 );
782 }
783
784 #[test]
785 fn test_tickdelta_subtraction() {
786 let delta = TickDelta::from(10i32);
787 let sub = delta - TickDelta::from(20i32);
788 assert_relative_eq!(sub.to_f32(), -10.0);
789
790 let a = TickDelta::lit("0.1");
791 let b = TickDelta::lit("0.6");
792 let sub = a - b;
793 assert_relative_eq!(sub.to_f32(), -0.5);
794
795 let a = TickDelta::lit("0.8");
797 let b = TickDelta::lit("0.3");
798 let sub = a - b;
799 assert_relative_eq!(sub.to_f32(), 0.5);
800
801 let a = TickDelta::lit("2.7");
803 let b = TickDelta::lit("1.2");
804 let sub = a - b;
805 assert_relative_eq!(sub.to_f32(), 1.5);
806
807 let a = TickDelta::lit("2.1");
809 let b = TickDelta::lit("1.6");
810 let sub = a - b;
811 assert_relative_eq!(sub.to_f32(), 0.5);
812
813 let a = TickDelta::lit("1.2");
815 let b = TickDelta::lit("2.7");
816 let sub = a - b;
817 assert_relative_eq!(sub.to_f32(), -1.5);
818
819 let a = TickDelta::lit("1.6");
821 let b = TickDelta::lit("2.1");
822 let sub = a - b;
823 assert_relative_eq!(sub.to_f32(), -0.5);
824 }
825
826 #[test]
827 fn test_tick_conversion_roundtrip() {
828 let tick_duration = Duration::from_millis(100);
829 let original = TickInstant::lit("15.4");
830
831 let duration = original.as_duration(tick_duration);
833 let roundtrip = TickInstant::from_duration(duration, tick_duration);
834
835 assert_eq!(roundtrip.tick(), original.tick());
837
838 assert!((roundtrip.overstep().to_f32() - original.overstep().to_f32()).abs() < 0.01);
839 }
840
841 #[test]
842 fn test_tickdelta_conversion_roundtrip() {
843 let tick_duration = Duration::from_millis(100);
844
845 let original_delta = TickDelta::lit("5.3");
847 let time_delta = original_delta.to_time_delta(tick_duration);
848 let roundtrip = TickDelta::from_time_delta(time_delta, tick_duration);
849
850 assert_eq!(roundtrip.tick_diff(), original_delta.tick_diff());
851 assert!((roundtrip.overstep().to_f32() - original_delta.overstep().to_f32()).abs() < 0.01);
852 assert_eq!(roundtrip.is_negative(), original_delta.is_negative());
853
854 let original_delta = TickDelta::lit("-7.6");
856 let time_delta = original_delta.to_time_delta(tick_duration);
857 let roundtrip = TickDelta::from_time_delta(time_delta, tick_duration);
858
859 assert_eq!(roundtrip.tick_diff(), original_delta.tick_diff());
860 assert!((roundtrip.overstep().to_f32() - original_delta.overstep().to_f32()).abs() < 0.01);
861 assert_eq!(roundtrip.is_negative(), original_delta.is_negative());
862 }
863}