1pub struct FixedFormat<I, const FRAC: usize>(core::marker::PhantomData<I>);
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub struct Value<I, const FRAC: usize> {
12 pub value: I,
14}
15
16macro_rules! impl_fixed_format {
17 ($I:ty, $is_signed:expr, $bits:expr) => {
18 impl<const FRAC: usize> FixedFormat<$I, FRAC> {
19 pub const BITS: usize = $bits;
20 pub const FRACTIONAL_BITS: usize = FRAC;
21 pub const IS_SIGNED: bool = $is_signed;
22 pub const IS_UNSIGNED: bool = !$is_signed;
23 const _ASSERT_STRICT_FORMAT: () = {
24 let bits = core::mem::size_of::<$I>() * 8;
25 if $is_signed {
26 assert!(
27 FRAC < bits,
28 "The number of fractional bits must be less than the total bit-width for signed types."
29 );
30 } else {
31 assert!(
32 FRAC <= bits,
33 "The number of fractional bits must be less than or equal to the total bit-width for unsigned types."
34 );
35 }
36 };
37 pub const INTEGRAL_BITS: usize = {
38 let _ = Self::_ASSERT_STRICT_FORMAT;
39 $bits - FRAC - (if $is_signed { 1 } else { 0 })
40 };
41 pub const POWER: usize =
42 if FRAC >= $bits { 0 } else { 1usize.wrapping_shl(FRAC as u32) };
43 pub const FRACTIONAL_MASK: $I = if FRAC >= $bits {
44 !0 as $I
45 } else {
46 (1 as $I).wrapping_shl(FRAC as u32).wrapping_sub(1)
47 };
48 pub const INTEGRAL_MASK: $I = !Self::FRACTIONAL_MASK;
49 pub const SIGN_BIT: $I =
50 if $is_signed { (1 as $I).wrapping_shl(($bits - 1) as u32) } else { 0 };
51 pub const BINARY_POINT: $I =
52 if FRAC > 0 { (1 as $I).wrapping_shl((FRAC - 1) as u32) } else { 0 };
53 pub const ONES_PLACE: $I = (1 as $I).wrapping_shl(FRAC as u32);
54 pub const APPROXIMATE_UNIT: bool = ($is_signed && FRAC == $bits - 1) || FRAC == $bits;
55 pub const ADJUSTED_FRACTIONAL_BITS: usize =
56 FRAC - (if Self::APPROXIMATE_UNIT { 1 } else { 0 });
57 pub const ADJUSTED_POWER: $I =
58 (1 as $I).wrapping_shl(Self::ADJUSTED_FRACTIONAL_BITS as u32);
59 pub const ADJUSTMENT_FACTOR: $I =
60 if Self::APPROXIMATE_UNIT { 2 as $I } else { 1 as $I };
61 pub const ADJUSTED_FRACTIONAL_MASK: $I =
62 (1 as $I).wrapping_shl(Self::ADJUSTED_FRACTIONAL_BITS as u32).wrapping_sub(1);
63 pub const ADJUSTED_INTEGRAL_MASK: $I = !Self::ADJUSTED_FRACTIONAL_MASK;
64 pub const MIN: $I = <$I>::MIN;
65 pub const MAX: $I = <$I>::MAX;
66 pub const INTEGRAL_MIN: $I =
67 if FRAC >= $bits { 0 } else { ((<$I>::MIN as i128) / (Self::POWER as i128)) as $I };
68 pub const INTEGRAL_MAX: $I =
69 if FRAC >= $bits { 0 } else { ((<$I>::MAX as i128) / (Self::POWER as i128)) as $I };
70
71
72
73 pub fn round(value: $I) -> $I {
74 crate::utility::round_half_to_even::<$I>(value, FRAC)
75 }
76
77 pub fn round_to_place(value: $I, place: usize) -> $I {
78 crate::utility::round_half_to_even::<$I>(value, place)
79 }
80 }
81 };
82}
83
84impl_fixed_format!(i8, true, 8);
85impl_fixed_format!(i16, true, 16);
86impl_fixed_format!(i32, true, 32);
87impl_fixed_format!(i64, true, 64);
88impl_fixed_format!(u8, false, 8);
89impl_fixed_format!(u16, false, 16);
90impl_fixed_format!(u32, false, 32);
91impl_fixed_format!(u64, false, 64);
92
93pub trait ConvertValue<IDst, const FRAC_DST: usize> {
96 fn convert_value(self) -> Value<IDst, FRAC_DST>;
98}
99
100macro_rules! impl_convert_value {
101 (unsigned: $I_SRC:ty, $I_DST:ty) => {
102 impl<const FRAC_SRC: usize, const FRAC_DST: usize> ConvertValue<$I_DST, FRAC_DST>
103 for Value<$I_SRC, FRAC_SRC>
104 {
105 fn convert_value(self) -> Value<$I_DST, FRAC_DST> {
106 let val = self.value;
107 let promoted_value = val as i128;
108
109 let src_adj_frac = FixedFormat::<$I_SRC, FRAC_SRC>::ADJUSTED_FRACTIONAL_BITS;
110 let _dst_adj_frac = FixedFormat::<$I_DST, FRAC_DST>::ADJUSTED_FRACTIONAL_BITS;
111
112 let src_adj_factor = FixedFormat::<$I_SRC, FRAC_SRC>::ADJUSTMENT_FACTOR as i128;
113 let _dst_adj_factor = FixedFormat::<$I_DST, FRAC_DST>::ADJUSTMENT_FACTOR as i128;
114
115 if FRAC_SRC > FRAC_DST {
116 let shifted_value = promoted_value / src_adj_factor;
117 let delta = src_adj_frac - FRAC_DST;
118 let rounded = crate::utility::round_half_to_even::<i128>(shifted_value, delta);
119 let power = 1i128 << delta;
120 let converted_value = rounded / power;
121 Value { value: crate::utility::clamp_cast(converted_value) }
122 } else if FRAC_SRC < FRAC_DST {
123 let delta = FRAC_DST - FRAC_SRC;
124 let power = 1i128 << delta;
125 let converted_value: $I_DST =
126 crate::saturating_arithmetic::saturate_multiply(promoted_value, power);
127 Value { value: converted_value }
128 } else {
129 Value { value: crate::utility::clamp_cast(promoted_value) }
130 }
131 }
132 }
133 };
134 (mixed: $I_SRC:ty, $I_DST:ty) => {
135 impl<const FRAC_SRC: usize, const FRAC_DST: usize> ConvertValue<$I_DST, FRAC_DST>
136 for Value<$I_SRC, FRAC_SRC>
137 {
138 fn convert_value(self) -> Value<$I_DST, FRAC_DST> {
139 let val = self.value;
140 let promoted_value: i128 = crate::utility::clamp_cast(val);
141
142 let src_adj_frac = FixedFormat::<$I_SRC, FRAC_SRC>::ADJUSTED_FRACTIONAL_BITS;
143 let _dst_adj_frac = FixedFormat::<$I_DST, FRAC_DST>::ADJUSTED_FRACTIONAL_BITS;
144
145 let src_adj_factor = FixedFormat::<$I_SRC, FRAC_SRC>::ADJUSTMENT_FACTOR as i128;
146 let _dst_adj_factor = FixedFormat::<$I_DST, FRAC_DST>::ADJUSTMENT_FACTOR as i128;
147
148 if FRAC_SRC > FRAC_DST {
149 let shifted_value = promoted_value / src_adj_factor;
150 let delta = src_adj_frac - FRAC_DST;
151 let rounded = crate::utility::round_half_to_even::<i128>(shifted_value, delta);
152 let power = 1i128 << delta;
153 let converted_value = rounded / power;
154 Value { value: crate::utility::clamp_cast(converted_value) }
155 } else if FRAC_SRC < FRAC_DST {
156 let delta = FRAC_DST - FRAC_SRC;
157 let power = 1i128 << delta;
158 let converted_value: $I_DST =
159 crate::saturating_arithmetic::saturate_multiply(promoted_value, power);
160 Value { value: converted_value }
161 } else {
162 Value { value: crate::utility::clamp_cast(promoted_value) }
163 }
164 }
165 }
166 };
167}
168
169macro_rules! gen_convert_values {
170 (unsigned: [$($T:ty),*], $U_list:tt) => {
171 $(
172 gen_convert_values!(@unsigned_one $T, $U_list);
173 )*
174 };
175 (@unsigned_one $T:ty, [$($U:ty),*]) => {
176 $(
177 impl_convert_value!(unsigned: $T, $U);
178 )*
179 };
180
181 (mixed: [$($T:ty),*], $U_list:tt) => {
182 $(
183 gen_convert_values!(@mixed_one $T, $U_list);
184 )*
185 };
186 (@mixed_one $T:ty, [$($U:ty),*]) => {
187 $(
188 impl_convert_value!(mixed: $T, $U);
189 )*
190 };
191}
192
193gen_convert_values!(unsigned: [u8, u16, u32, u64], [u8, u16, u32, u64]);
194gen_convert_values!(mixed: [i8, i16, i32, i64], [i8, i16, i32, i64]);
195gen_convert_values!(mixed: [i8, i16, i32, i64], [u8, u16, u32, u64]);
196gen_convert_values!(mixed: [u8, u16, u32, u64], [i8, i16, i32, i64]);
197
198macro_rules! impl_convert_value_i128 {
199 ($I_SRC:ty) => {
200 impl<const FRAC_SRC: usize, const FRAC_DST: usize> ConvertValue<i128, FRAC_DST>
201 for Value<$I_SRC, FRAC_SRC>
202 {
203 fn convert_value(self) -> Value<i128, FRAC_DST> {
204 let val = self.value;
205 let promoted_value: i128 = crate::utility::clamp_cast(val);
206
207 let src_adj_frac = FixedFormat::<$I_SRC, FRAC_SRC>::ADJUSTED_FRACTIONAL_BITS;
208 let src_adj_factor = FixedFormat::<$I_SRC, FRAC_SRC>::ADJUSTMENT_FACTOR as i128;
209
210 if FRAC_SRC > FRAC_DST {
211 let shifted_value = promoted_value / src_adj_factor;
212 let delta = src_adj_frac - FRAC_DST;
213 let rounded = crate::utility::round_half_to_even::<i128>(shifted_value, delta);
214 let power = 1i128 << delta;
215 let converted_value = rounded / power;
216 Value { value: converted_value }
217 } else if FRAC_SRC < FRAC_DST {
218 let delta = FRAC_DST - FRAC_SRC;
219 let power = 1i128 << delta;
220 let converted_value: i128 =
221 crate::saturating_arithmetic::saturate_multiply(promoted_value, power);
222 Value { value: converted_value }
223 } else {
224 Value { value: promoted_value }
225 }
226 }
227 }
228 };
229}
230
231impl_convert_value_i128!(i8);
232impl_convert_value_i128!(i16);
233impl_convert_value_i128!(i32);
234impl_convert_value_i128!(i64);
235impl_convert_value_i128!(u8);
236impl_convert_value_i128!(u16);
237impl_convert_value_i128!(u32);
238impl_convert_value_i128!(u64);