1use crate::fixed_format::{ConvertValue, Value};
6use crate::num_traits;
7
8#[repr(transparent)]
19#[derive(Default, Clone, Copy, Hash)]
20pub struct Fixed<I, const FRAC: usize> {
21 value: I,
22}
23
24impl<I: Copy, const FRAC: usize> Fixed<I, FRAC> {
25 const _ASSERT_VALID_FORMAT: () = {
26 let bits = core::mem::size_of::<I>() * 8;
27 assert!(bits <= 64, "The Integer template parameter must have at most 64 bits!");
28 assert!(FRAC <= bits, "The number of fractional bits cannot exceed the integer bit-width!");
29 };
30
31 pub const fn from_raw(value: I) -> Self {
33 let () = Self::_ASSERT_VALID_FORMAT;
34 Self { value }
35 }
36
37 pub const fn raw_value(&self) -> I {
39 self.value
40 }
41
42 pub const fn value(&self) -> Value<I, FRAC> {
44 Value { value: self.value }
45 }
46
47 pub fn from_ratio(numerator: I, denominator: I) -> Self
49 where
50 I: num_traits::Bounded + num_traits::NumCast + Copy,
51 Value<I, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
52 {
53 let expr = crate::expression::from_ratio(numerator, denominator);
54 Fixed::from(expr)
55 }
56
57 pub fn mul_to<U, const FRAC_R: usize, R, const FRAC_OUT: usize>(
59 self,
60 other: Fixed<U, FRAC_R>,
61 ) -> Fixed<R, FRAC_OUT>
62 where
63 I: Into<i128> + Copy,
64 U: Into<i128> + Copy,
65 R: num_traits::Bounded + num_traits::NumCast + Copy,
66 {
67 let val_l: i128 = self.value.into();
68 let val_r: i128 = other.value.into();
69
70 let neg = (val_l < 0) ^ (val_r < 0);
71 let a_abs = val_l.unsigned_abs();
72 let b_abs = val_r.unsigned_abs();
73 let prod_abs = a_abs.saturating_mul(b_abs);
74
75 let src_frac = FRAC + FRAC_R;
76 let dst_frac = FRAC_OUT;
77
78 let scaled_abs = if src_frac > dst_frac {
79 let delta = src_frac - dst_frac;
80 let rounded = crate::utility::round_half_to_even::<u128>(prod_abs, delta);
81 if let Some(power) = 1u128.checked_shl(delta as u32) { rounded / power } else { 0 }
82 } else if src_frac < dst_frac {
83 let delta = dst_frac - src_frac;
84 if prod_abs <= (u128::MAX >> delta) { prod_abs << delta } else { u128::MAX }
85 } else {
86 prod_abs
87 };
88
89 let converted: R = if neg {
91 let scaled_i128 = if scaled_abs > i128::MIN.unsigned_abs() {
92 i128::MIN
93 } else {
94 (scaled_abs as i128).wrapping_neg()
95 };
96 crate::utility::clamp_cast(scaled_i128)
97 } else {
98 crate::utility::clamp_cast_u128(scaled_abs)
99 };
100
101 Fixed::from_raw(converted)
102 }
103}
104
105impl<I: num_traits::Bounded + num_traits::NumCast + Copy, const FRAC: usize> Fixed<I, FRAC> {
106 pub fn min() -> Self {
108 Self::from_raw(I::min_value())
109 }
110
111 pub fn max() -> Self {
113 Self::from_raw(I::max_value())
114 }
115
116 pub fn epsilon() -> Self
118 where
119 I: num_traits::One,
120 {
121 Self::from_raw(I::one())
122 }
123
124 pub fn from_integer(val: i128) -> Self
126 where
127 I: num_traits::Bounded + num_traits::NumCast + Copy,
128 Value<I, 0>: crate::fixed_format::ConvertValue<I, FRAC>,
129 {
130 let val_i: I = crate::utility::clamp_cast(val);
131 Self::from(Value::<I, 0> { value: val_i }.convert_value())
132 }
133
134 pub fn from_fixed<OtherI: Copy, const OTHER_FRAC: usize>(
136 other: Fixed<OtherI, OTHER_FRAC>,
137 ) -> Self
138 where
139 Value<OtherI, OTHER_FRAC>: crate::fixed_format::ConvertValue<I, FRAC>,
140 {
141 Self::from(other.value().convert_value())
142 }
143}
144
145impl<I: Copy, const FRAC: usize> From<Value<I, FRAC>> for Fixed<I, FRAC> {
146 fn from(val: Value<I, FRAC>) -> Self {
147 Self::from_raw(val.value)
148 }
149}
150
151#[inline]
152fn safe_shl_u128(val: u128, shift: usize) -> Option<u128> {
153 if val == 0 {
154 return Some(0);
155 }
156 if shift >= 128 {
157 None
158 } else {
159 if val <= (u128::MAX >> shift) { Some(val << shift) } else { None }
160 }
161}
162
163#[inline]
164fn safe_shl_i128(val: i128, shift: usize) -> Option<i128> {
165 if val == 0 {
166 return Some(0);
167 }
168 if val > 0 {
169 if shift >= 127 {
170 None
171 } else {
172 if (val as u128) <= ((i128::MAX as u128) >> shift) { Some(val << shift) } else { None }
173 }
174 } else {
175 let abs = val.unsigned_abs();
176 let safe = if shift >= 128 { false } else { abs <= (1u128 << (127 - shift)) };
177 if safe { Some(val << shift) } else { None }
178 }
179}
180
181pub(crate) fn compare_raw(
182 lhs_raw: i128,
183 f1: usize,
184 rhs_raw: i128,
185 f2: usize,
186) -> core::cmp::Ordering {
187 if lhs_raw < 0 && rhs_raw >= 0 {
188 return core::cmp::Ordering::Less;
189 }
190 if lhs_raw >= 0 && rhs_raw < 0 {
191 return core::cmp::Ordering::Greater;
192 }
193 if f1 == f2 {
194 return lhs_raw.cmp(&rhs_raw);
195 }
196 if lhs_raw >= 0 {
197 let l = lhs_raw as u128;
198 let r = rhs_raw as u128;
199 if f1 > f2 {
200 if let Some(val) = safe_shl_u128(r, f1 - f2) {
201 l.cmp(&val)
202 } else {
203 core::cmp::Ordering::Less
204 }
205 } else {
206 if let Some(val) = safe_shl_u128(l, f2 - f1) {
207 val.cmp(&r)
208 } else {
209 core::cmp::Ordering::Greater
210 }
211 }
212 } else {
213 if f1 > f2 {
214 if let Some(val) = safe_shl_i128(rhs_raw, f1 - f2) {
215 lhs_raw.cmp(&val)
216 } else {
217 core::cmp::Ordering::Greater
218 }
219 } else {
220 if let Some(val) = safe_shl_i128(lhs_raw, f2 - f1) {
221 val.cmp(&rhs_raw)
222 } else {
223 core::cmp::Ordering::Less
224 }
225 }
226 }
227}
228
229impl<I, const F1: usize, OtherI, const F2: usize> PartialEq<Fixed<OtherI, F2>> for Fixed<I, F1>
232where
233 I: Into<i128> + Copy,
234 OtherI: Into<i128> + Copy,
235{
236 fn eq(&self, other: &Fixed<OtherI, F2>) -> bool {
237 let lhs_i: i128 = self.raw_value().into();
238 let rhs_i: i128 = other.raw_value().into();
239 compare_raw(lhs_i, F1, rhs_i, F2) == core::cmp::Ordering::Equal
240 }
241}
242
243impl<I, const F1: usize, OtherI, const F2: usize> PartialOrd<Fixed<OtherI, F2>> for Fixed<I, F1>
244where
245 I: Into<i128> + Copy,
246 OtherI: Into<i128> + Copy,
247{
248 fn partial_cmp(&self, other: &Fixed<OtherI, F2>) -> Option<core::cmp::Ordering> {
249 let lhs_i: i128 = self.raw_value().into();
250 let rhs_i: i128 = other.raw_value().into();
251 Some(compare_raw(lhs_i, F1, rhs_i, F2))
252 }
253}
254
255pub fn compare_fixed<I1, const F1: usize, I2, const F2: usize>(
258 lhs: Fixed<I1, F1>,
259 rhs: Fixed<I2, F2>,
260) -> core::cmp::Ordering
261where
262 I1: Into<i128> + Copy,
263 I2: Into<i128> + Copy,
264{
265 let lhs_i: i128 = lhs.raw_value().into();
266 let rhs_i: i128 = rhs.raw_value().into();
267 compare_raw(lhs_i, F1, rhs_i, F2)
268}
269
270macro_rules! impl_fixed_integer_comparison {
272 ($T:ty, $Int:ty) => {
273 impl<const FRAC: usize> PartialEq<$Int> for Fixed<$T, FRAC>
274 where
275 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
276 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
277 {
278 fn eq(&self, other: &$Int) -> bool {
279 let lhs: Value<i128, FRAC> = self.value().convert_value();
280 let rhs: Value<i128, FRAC> = Value::<$Int, 0> { value: *other }.convert_value();
281 lhs.value == rhs.value
282 }
283 }
284
285 impl<const FRAC: usize> PartialEq<Fixed<$T, FRAC>> for $Int
286 where
287 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
288 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
289 {
290 fn eq(&self, other: &Fixed<$T, FRAC>) -> bool {
291 other.eq(self)
292 }
293 }
294
295 impl<const FRAC: usize> PartialOrd<$Int> for Fixed<$T, FRAC>
296 where
297 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
298 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
299 {
300 fn partial_cmp(&self, other: &$Int) -> Option<core::cmp::Ordering> {
301 let lhs: Value<i128, FRAC> = self.value().convert_value();
302 let rhs: Value<i128, FRAC> = Value::<$Int, 0> { value: *other }.convert_value();
303 lhs.value.partial_cmp(&rhs.value)
304 }
305 }
306
307 impl<const FRAC: usize> PartialOrd<Fixed<$T, FRAC>> for $Int
308 where
309 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
310 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
311 {
312 fn partial_cmp(&self, other: &Fixed<$T, FRAC>) -> Option<core::cmp::Ordering> {
313 other.partial_cmp(self).map(|o| o.reverse())
314 }
315 }
316 };
317}
318
319macro_rules! gen_fixed_integer_comparisons {
320 (signed: [$($T:ty),*], $Int_list:tt) => {
321 $(
322 gen_fixed_integer_comparisons!(@signed_one $T, $Int_list);
323 )*
324 };
325 (@signed_one $T:ty, [$($Int:ty),*]) => {
326 $(
327 impl_fixed_integer_comparison!($T, $Int);
328 )*
329 };
330
331 (unsigned: [$($T:ty),*], $Int_list:tt) => {
332 $(
333 gen_fixed_integer_comparisons!(@unsigned_one $T, $Int_list);
334 )*
335 };
336 (@unsigned_one $T:ty, [$($Int:ty),*]) => {
337 $(
338 impl_fixed_integer_comparison!($T, $Int);
339 )*
340 };
341
342 (mixed: [$($T:ty),*], $Int_list:tt) => {
343 $(
344 gen_fixed_integer_comparisons!(@mixed_one $T, $Int_list);
345 )*
346 };
347 (@mixed_one $T:ty, [$($Int:ty),*]) => {
348 $(
349 impl_fixed_integer_comparison!($T, $Int);
350 )*
351 };
352}
353
354gen_fixed_integer_comparisons!(signed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
355gen_fixed_integer_comparisons!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
356gen_fixed_integer_comparisons!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
357gen_fixed_integer_comparisons!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);
358
359impl<I, const FRAC: usize> Eq for Fixed<I, FRAC> where I: Into<i128> + Copy {}
360
361impl<I, const FRAC: usize> Ord for Fixed<I, FRAC>
364where
365 I: Into<i128> + Copy,
366{
367 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
368 compare_fixed(*self, *other)
369 }
370}
371
372impl<I, const FRAC: usize> Fixed<I, FRAC> {
374 pub fn ceiling(&self) -> I
376 where
377 I: num_traits::Bounded
378 + num_traits::NumCast
379 + Copy
380 + Into<i128>
381 + num_traits::One
382 + num_traits::Zero,
383 {
384 let raw_i: i128 = self.value.into();
385 let power = if let Some(p) = 1i128.checked_shl(FRAC as u32) {
386 p
387 } else {
388 return if raw_i > 0 { I::one() } else { I::zero() };
389 };
390 let div = raw_i / power;
391 let rem = raw_i % power;
392
393 let res = if rem != 0 && raw_i > 0 { div + 1 } else { div };
394 crate::utility::clamp_cast(res)
395 }
396
397 pub fn floor(&self) -> I
399 where
400 I: num_traits::Bounded + num_traits::NumCast + Copy + Into<i128> + num_traits::Zero,
401 {
402 let raw_i: i128 = self.value.into();
403 let power = if let Some(p) = 1i128.checked_shl(FRAC as u32) {
404 p
405 } else {
406 return if raw_i < 0 { crate::utility::clamp_cast(-1i128) } else { I::zero() };
407 };
408 let div = raw_i / power;
409 let rem = raw_i % power;
410
411 let res = if rem != 0 && raw_i < 0 { div - 1 } else { div };
412 crate::utility::clamp_cast(res)
413 }
414
415 pub fn round(&self) -> I
417 where
418 I: num_traits::Bounded + num_traits::NumCast + Copy + Into<i128> + num_traits::Zero,
419 {
420 let raw_i: i128 = self.value.into();
421 let rounded = crate::utility::round_half_to_even::<i128>(raw_i, FRAC);
422 let power = if let Some(p) = 1i128.checked_shl(FRAC as u32) {
423 p
424 } else {
425 return I::zero();
426 };
427 let rounded_int = rounded / power;
428 crate::utility::clamp_cast(rounded_int)
429 }
430
431 pub fn integral(&self) -> Self
433 where
434 I: num_traits::Bounded + num_traits::NumCast + PartialOrd + Copy,
435 {
436 if FRAC == 0 {
437 return *self;
438 }
439 let raw_i: i128 = self.value.into();
440 let mask = (!0u128).checked_shl(FRAC as u32).unwrap_or(0);
441 let abs_integral = raw_i.unsigned_abs() & mask;
442 let raw_integral = if raw_i < 0 { -(abs_integral as i128) } else { abs_integral as i128 };
443 Self::from_raw(crate::utility::clamp_cast(raw_integral))
444 }
445
446 pub fn fraction(&self) -> Self
448 where
449 I: num_traits::Bounded
450 + num_traits::NumCast
451 + PartialOrd
452 + Copy
453 + num_traits::Zero
454 + core::ops::Sub<Output = I>,
455 {
456 if FRAC == 0 {
457 return Self::from_raw(I::zero());
458 }
459 Self::from_raw(self.value - self.integral().value)
460 }
461
462 pub fn absolute(&self) -> Self
468 where
469 I: num_traits::Bounded + num_traits::NumCast + Copy + Into<i128>,
470 {
471 let raw_i: i128 = self.value.into();
472 if raw_i >= 0 {
473 return *self;
474 }
475 let abs_raw = raw_i.unsigned_abs();
476 Self::from_raw(crate::utility::clamp_cast_u128(abs_raw))
477 }
478}
479
480pub trait PromoteAddSub<U> {
482 type Output;
483}
484pub trait PromoteMul<U> {
485 type Output;
486}
487
488macro_rules! impl_promotes {
489 ($T:ty, $U:ty => $AddSub:ty, $Mul:ty) => {
490 impl PromoteAddSub<$U> for $T {
491 type Output = $AddSub;
492 }
493 impl PromoteAddSub<$T> for $U {
494 type Output = $AddSub;
495 }
496 impl PromoteMul<$U> for $T {
497 type Output = $Mul;
498 }
499 impl PromoteMul<$T> for $U {
500 type Output = $Mul;
501 }
502 };
503}
504
505macro_rules! impl_promotes_same {
506 ($T:ty => $AddSub:ty, $Mul:ty) => {
507 impl PromoteAddSub<$T> for $T {
508 type Output = $AddSub;
509 }
510 impl PromoteMul<$T> for $T {
511 type Output = $Mul;
512 }
513 };
514}
515
516impl_promotes_same!(u8 => u16, u16);
517impl_promotes_same!(u16 => u32, u32);
518impl_promotes_same!(u32 => u64, u64);
519impl_promotes_same!(u64 => u64, u64);
520
521impl_promotes_same!(i8 => i16, i16);
522impl_promotes_same!(i16 => i32, i32);
523impl_promotes_same!(i32 => i64, i64);
524impl_promotes_same!(i64 => i64, i64);
525
526impl_promotes!(u8, u16 => u32, u32);
527impl_promotes!(u8, u32 => u64, u64);
528impl_promotes!(u8, u64 => u64, u64);
529impl_promotes!(u16, u32 => u64, u64);
530impl_promotes!(u16, u64 => u64, u64);
531impl_promotes!(u32, u64 => u64, u64);
532
533impl_promotes!(i8, i16 => i32, i32);
534impl_promotes!(i8, i32 => i64, i64);
535impl_promotes!(i8, i64 => i64, i64);
536impl_promotes!(i16, i32 => i64, i64);
537impl_promotes!(i16, i64 => i64, i64);
538impl_promotes!(i32, i64 => i64, i64);
539
540impl_promotes!(i8, u8 => i16, i32);
541impl_promotes!(i8, u16 => i32, i32);
542impl_promotes!(i8, u32 => i64, i64);
543impl_promotes!(i8, u64 => i64, i64);
544
545impl_promotes!(i16, u8 => i32, i32);
546impl_promotes!(i16, u16 => i32, i32);
547impl_promotes!(i16, u32 => i64, i64);
548impl_promotes!(i16, u64 => i64, i64);
549
550impl_promotes!(i32, u8 => i64, i64);
551impl_promotes!(i32, u16 => i64, i64);
552impl_promotes!(i32, u32 => i64, i64);
553impl_promotes!(i32, u64 => i64, i64);
554
555impl_promotes!(i64, u8 => i64, i64);
556impl_promotes!(i64, u16 => i64, i64);
557impl_promotes!(i64, u32 => i64, i64);
558impl_promotes!(i64, u64 => i64, i64);
559
560impl<T, const FRAC: usize, U, const FRAC_R: usize> core::ops::Add<Fixed<U, FRAC_R>>
562 for Fixed<T, FRAC>
563where
564 T: PromoteAddSub<U> + Copy,
565 U: Copy,
566 T::Output: num_traits::Bounded + num_traits::NumCast + Copy,
567 Value<T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
568 Value<U, FRAC_R>: crate::fixed_format::ConvertValue<i128, FRAC>,
569{
570 type Output = Fixed<T::Output, FRAC>;
571
572 fn add(self, rhs: Fixed<U, FRAC_R>) -> Self::Output {
573 let lhs_promoted: Value<i128, FRAC> = self.value().convert_value();
574 let rhs_promoted: Value<i128, FRAC> = rhs.value().convert_value();
575 let sum: i128 =
576 crate::saturating_arithmetic::saturate_add(lhs_promoted.value, rhs_promoted.value);
577 Fixed::from_raw(crate::utility::clamp_cast(sum))
578 }
579}
580
581impl<T, const FRAC: usize, U, const FRAC_R: usize> core::ops::Sub<Fixed<U, FRAC_R>>
582 for Fixed<T, FRAC>
583where
584 T: PromoteAddSub<U> + Copy,
585 U: Copy,
586 T::Output: num_traits::Bounded + num_traits::NumCast + Copy,
587 Value<T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
588 Value<U, FRAC_R>: crate::fixed_format::ConvertValue<i128, FRAC>,
589{
590 type Output = Fixed<T::Output, FRAC>;
591
592 fn sub(self, rhs: Fixed<U, FRAC_R>) -> Self::Output {
593 let lhs_promoted: Value<i128, FRAC> = self.value().convert_value();
594 let rhs_promoted: Value<i128, FRAC> = rhs.value().convert_value();
595 let diff: i128 =
596 crate::saturating_arithmetic::saturate_subtract(lhs_promoted.value, rhs_promoted.value);
597 Fixed::from_raw(crate::utility::clamp_cast(diff))
598 }
599}
600
601impl<T, const FRAC: usize, U, const FRAC_R: usize> core::ops::Mul<Fixed<U, FRAC_R>>
602 for Fixed<T, FRAC>
603where
604 T: PromoteMul<U> + Into<i128> + Copy,
605 U: Into<i128> + Copy,
606 T::Output: num_traits::Bounded + num_traits::NumCast + Copy,
607{
608 type Output = Fixed<T::Output, FRAC>;
609
610 fn mul(self, rhs: Fixed<U, FRAC_R>) -> Self::Output {
611 self.mul_to::<U, FRAC_R, T::Output, FRAC>(rhs)
612 }
613}
614
615impl<I, const FRAC: usize> core::ops::Neg for Fixed<I, FRAC>
616where
617 I: num_traits::Bounded + num_traits::NumCast + Copy + num_traits::Signed + Into<i128>,
618{
619 type Output = Self;
620
621 fn neg(self) -> Self::Output {
622 let zero = 0i128;
623 let negated = crate::saturating_arithmetic::saturate_subtract(zero, self.value);
624 Self::from_raw(negated)
625 }
626}
627
628macro_rules! impl_fixed_integer_arithmetic {
630 ($T:ty, $Int:ty) => {
631 impl<const FRAC: usize> core::ops::Add<$Int> for Fixed<$T, FRAC>
632 where
633 $T: PromoteAddSub<$Int> + Copy,
634 $Int: Copy,
635 <$T as PromoteAddSub<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
636 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
637 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
638 {
639 type Output = Fixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
640
641 fn add(self, rhs: $Int) -> Self::Output {
642 let lhs_promoted: Value<i128, FRAC> = self.value().convert_value();
643 let rhs_promoted: Value<i128, FRAC> =
644 Value::<$Int, 0> { value: rhs }.convert_value();
645 let sum: i128 = crate::saturating_arithmetic::saturate_add(
646 lhs_promoted.value,
647 rhs_promoted.value,
648 );
649 Fixed::from_raw(crate::utility::clamp_cast(sum))
650 }
651 }
652
653 impl<const FRAC: usize> core::ops::Add<Fixed<$T, FRAC>> for $Int
654 where
655 $T: PromoteAddSub<$Int> + Copy,
656 $Int: Copy,
657 <$T as PromoteAddSub<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
658 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
659 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
660 {
661 type Output = Fixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
662
663 fn add(self, rhs: Fixed<$T, FRAC>) -> Self::Output {
664 let lhs_promoted: Value<i128, FRAC> =
665 Value::<$Int, 0> { value: self }.convert_value();
666 let rhs_promoted: Value<i128, FRAC> = rhs.value().convert_value();
667 let sum: i128 = crate::saturating_arithmetic::saturate_add(
668 lhs_promoted.value,
669 rhs_promoted.value,
670 );
671 Fixed::from_raw(crate::utility::clamp_cast(sum))
672 }
673 }
674
675 impl<const FRAC: usize> core::ops::Sub<$Int> for Fixed<$T, FRAC>
676 where
677 $T: PromoteAddSub<$Int> + Copy,
678 $Int: Copy,
679 <$T as PromoteAddSub<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
680 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
681 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
682 {
683 type Output = Fixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
684
685 fn sub(self, rhs: $Int) -> Self::Output {
686 let lhs_promoted: Value<i128, FRAC> = self.value().convert_value();
687 let rhs_promoted: Value<i128, FRAC> =
688 Value::<$Int, 0> { value: rhs }.convert_value();
689 let diff: i128 = crate::saturating_arithmetic::saturate_subtract(
690 lhs_promoted.value,
691 rhs_promoted.value,
692 );
693 Fixed::from_raw(crate::utility::clamp_cast(diff))
694 }
695 }
696
697 impl<const FRAC: usize> core::ops::Sub<Fixed<$T, FRAC>> for $Int
698 where
699 $T: PromoteAddSub<$Int> + Copy,
700 $Int: Copy,
701 <$T as PromoteAddSub<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
702 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
703 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
704 {
705 type Output = Fixed<<$T as PromoteAddSub<$Int>>::Output, FRAC>;
706
707 fn sub(self, rhs: Fixed<$T, FRAC>) -> Self::Output {
708 let lhs_promoted: Value<i128, FRAC> =
709 Value::<$Int, 0> { value: self }.convert_value();
710 let rhs_promoted: Value<i128, FRAC> = rhs.value().convert_value();
711 let diff: i128 = crate::saturating_arithmetic::saturate_subtract(
712 lhs_promoted.value,
713 rhs_promoted.value,
714 );
715 Fixed::from_raw(crate::utility::clamp_cast(diff))
716 }
717 }
718
719 impl<const FRAC: usize> core::ops::Mul<$Int> for Fixed<$T, FRAC>
720 where
721 $T: PromoteMul<$Int> + Copy,
722 $Int: Copy,
723 <$T as PromoteMul<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
724 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
725 {
726 type Output = Fixed<<$T as PromoteMul<$Int>>::Output, FRAC>;
727
728 fn mul(self, rhs: $Int) -> Self::Output {
729 let lhs_promoted: Value<i128, FRAC> = self.value().convert_value();
730 let rhs_i128: i128 = rhs.into();
731 let prod: i128 =
732 crate::saturating_arithmetic::saturate_multiply(lhs_promoted.value, rhs_i128);
733 Fixed::from_raw(crate::utility::clamp_cast(prod))
734 }
735 }
736
737 impl<const FRAC: usize> core::ops::Mul<Fixed<$T, FRAC>> for $Int
738 where
739 $T: PromoteMul<$Int> + Copy,
740 $Int: Copy,
741 <$T as PromoteMul<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
742 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
743 {
744 type Output = Fixed<<$T as PromoteMul<$Int>>::Output, FRAC>;
745
746 fn mul(self, rhs: Fixed<$T, FRAC>) -> Self::Output {
747 rhs.mul(self)
748 }
749 }
750
751 impl<const FRAC: usize> core::ops::AddAssign<$Int> for Fixed<$T, FRAC>
752 where
753 $T: PromoteAddSub<$Int> + num_traits::Bounded + num_traits::NumCast + Copy,
754 $Int: Copy,
755 <$T as PromoteAddSub<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
756 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
757 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
758 {
759 fn add_assign(&mut self, rhs: $Int) {
760 let sum = core::ops::Add::add(*self, rhs);
761 self.value = crate::utility::clamp_cast(sum.raw_value());
762 }
763 }
764
765 impl<const FRAC: usize> core::ops::SubAssign<$Int> for Fixed<$T, FRAC>
766 where
767 $T: PromoteAddSub<$Int> + num_traits::Bounded + num_traits::NumCast + Copy,
768 $Int: Copy,
769 <$T as PromoteAddSub<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
770 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
771 Value<$Int, 0>: crate::fixed_format::ConvertValue<i128, FRAC>,
772 {
773 fn sub_assign(&mut self, rhs: $Int) {
774 let diff = core::ops::Sub::sub(*self, rhs);
775 self.value = crate::utility::clamp_cast(diff.raw_value());
776 }
777 }
778
779 impl<const FRAC: usize> core::ops::MulAssign<$Int> for Fixed<$T, FRAC>
780 where
781 $T: PromoteMul<$Int> + num_traits::Bounded + num_traits::NumCast + Copy,
782 $Int: Copy,
783 <$T as PromoteMul<$Int>>::Output: num_traits::Bounded + num_traits::NumCast + Copy,
784 Value<$T, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
785 {
786 fn mul_assign(&mut self, rhs: $Int) {
787 let prod = core::ops::Mul::mul(*self, rhs);
788 self.value = crate::utility::clamp_cast(prod.raw_value());
789 }
790 }
791 };
792}
793
794macro_rules! gen_fixed_integer_arithmetic {
795 (signed: [$($T:ty),*], $Int_list:tt) => {
796 $(
797 gen_fixed_integer_arithmetic!(@one $T, $Int_list);
798 )*
799 };
800 (unsigned: [$($T:ty),*], $Int_list:tt) => {
801 $(
802 gen_fixed_integer_arithmetic!(@one $T, $Int_list);
803 )*
804 };
805 (mixed: [$($T:ty),*], $Int_list:tt) => {
806 $(
807 gen_fixed_integer_arithmetic!(@one $T, $Int_list);
808 )*
809 };
810 (@one $T:ty, [$($Int:ty),*]) => {
811 $(
812 impl_fixed_integer_arithmetic!($T, $Int);
813 )*
814 };
815}
816
817gen_fixed_integer_arithmetic!(signed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
818gen_fixed_integer_arithmetic!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
819gen_fixed_integer_arithmetic!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
820gen_fixed_integer_arithmetic!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);
821impl<I, const FRAC: usize, U, const FRAC_R: usize> core::ops::AddAssign<Fixed<U, FRAC_R>>
823 for Fixed<I, FRAC>
824where
825 I: PromoteAddSub<U> + Copy + num_traits::Bounded + num_traits::NumCast + TryFrom<I::Output>,
826 U: Copy,
827 I::Output: num_traits::Bounded + num_traits::NumCast + Copy + PartialOrd + num_traits::Zero,
828 Value<I, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
829 Value<U, FRAC_R>: crate::fixed_format::ConvertValue<i128, FRAC>,
830{
831 fn add_assign(&mut self, rhs: Fixed<U, FRAC_R>) {
832 let sum = *self + rhs;
833 self.value = crate::utility::clamp_cast(sum.raw_value());
834 }
835}
836
837impl<I, const FRAC: usize, U, const FRAC_R: usize> core::ops::SubAssign<Fixed<U, FRAC_R>>
838 for Fixed<I, FRAC>
839where
840 I: PromoteAddSub<U> + Copy + num_traits::Bounded + num_traits::NumCast + TryFrom<I::Output>,
841 U: Copy,
842 I::Output: num_traits::Bounded + num_traits::NumCast + Copy + PartialOrd + num_traits::Zero,
843 Value<I, FRAC>: crate::fixed_format::ConvertValue<i128, FRAC>,
844 Value<U, FRAC_R>: crate::fixed_format::ConvertValue<i128, FRAC>,
845{
846 fn sub_assign(&mut self, rhs: Fixed<U, FRAC_R>) {
847 let diff = *self - rhs;
848 self.value = crate::utility::clamp_cast(diff.raw_value());
849 }
850}
851
852impl<I, const FRAC: usize> core::ops::MulAssign<Fixed<I, FRAC>> for Fixed<I, FRAC>
853where
854 I: num_traits::Bounded + num_traits::NumCast + Into<i128> + Copy,
855{
856 fn mul_assign(&mut self, rhs: Self) {
857 *self = self.mul_to(rhs);
858 }
859}
860
861impl<I, const FRAC: usize> core::ops::DivAssign<Fixed<I, FRAC>> for Fixed<I, FRAC>
862where
863 Self: Copy,
864 Fixed<I, FRAC>: From<crate::expression::DivExpression<Self, Self>>,
865{
866 fn div_assign(&mut self, rhs: Self) {
867 *self = Self::from(crate::expression::DivExpression { numerator: *self, denominator: rhs });
868 }
869}
870
871impl<I, const FRAC: usize> core::fmt::Display for Fixed<I, FRAC>
873where
874 I: Into<i128> + Copy,
875{
876 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
877 let s = crate::string::String::new(*self, crate::string::Mode::Dec, f.precision());
878 f.write_str(s.as_str())
879 }
880}
881
882impl<I, const FRAC: usize> core::fmt::Debug for Fixed<I, FRAC>
883where
884 I: Into<i128> + Copy + core::fmt::Debug,
885{
886 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
887 let s = crate::string::String::new(*self, crate::string::Mode::Dec, None);
888 write!(f, "Fixed(raw: {:?}, value: {})", self.raw_value(), s.as_str())
889 }
890}
891
892impl<I, const FRAC: usize> core::fmt::LowerHex for Fixed<I, FRAC>
893where
894 I: Into<i128> + Copy,
895{
896 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
897 let s = crate::string::String::new(*self, crate::string::Mode::Hex, Some(0));
898 f.write_str(s.as_str())
899 }
900}
901
902pub struct RationalFormatter<'a, I, const FRAC: usize>(&'a Fixed<I, FRAC>);
905
906impl<I, const FRAC: usize> core::fmt::Display for RationalFormatter<'_, I, FRAC>
907where
908 I: Into<i128> + Copy,
909{
910 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
911 let s = crate::string::String::new(*self.0, crate::string::Mode::DecRational, Some(0));
912 f.write_str(s.as_str())
913 }
914}
915
916impl<I, const FRAC: usize> Fixed<I, FRAC> {
917 pub fn rational(&self) -> RationalFormatter<'_, I, FRAC> {
920 RationalFormatter(self)
921 }
922}