1#[cfg(test)]
4mod tests;
5
6use std::{
7 fmt::{Debug, Display, Formatter},
8 ops::{
9 Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div,
10 DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
11 SubAssign,
12 },
13};
14
15#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
26#[repr(transparent)]
27pub struct Fixed(i32);
28
29macro_rules! fmul {
30 () => {
31 256.0
32 };
33}
34macro_rules! imul {
35 () => {
36 256
37 };
38}
39macro_rules! shift {
40 () => {
41 8
42 };
43}
44
45impl Fixed {
46 pub const MAX: Self = Self(i32::MAX);
48
49 pub const MIN: Self = Self(i32::MIN);
51
52 pub const ZERO: Self = Self(0);
54
55 pub const ONE: Self = Self::from_i32_saturating(1);
57
58 pub const TWO: Self = Self::from_i32_saturating(2);
60
61 pub const EPSILON: Self = Self(1);
63
64 pub const NEGATIVE_EPSILON: Self = Self(!0);
66
67 #[inline]
69 pub const fn from_wire(val: i32) -> Self {
70 Self(val)
71 }
72
73 #[inline]
75 pub const fn to_wire(self) -> i32 {
76 self.0
77 }
78
79 #[inline]
83 pub const fn to_f64(self) -> f64 {
84 self.0 as f64 / fmul!()
85 }
86
87 #[inline]
91 pub const fn to_f32_lossy(self) -> f32 {
92 self.to_f64() as f32
93 }
94
95 #[inline]
105 pub const fn from_f64_lossy(val: f64) -> Self {
106 Self((val * fmul!()) as i32)
107 }
108
109 #[inline]
113 pub const fn from_f32_lossy(val: f32) -> Self {
114 Self((val as f64 * fmul!()) as i32)
115 }
116
117 #[inline]
121 pub const fn from_i32_saturating(val: i32) -> Self {
122 Self(val.saturating_mul(imul!()))
123 }
124
125 #[inline]
129 pub const fn from_i64_saturating(val: i64) -> Self {
130 let val = val.saturating_mul(imul!());
131 if val > i32::MAX as i64 {
132 Self(i32::MAX)
133 } else if val < i32::MIN as i64 {
134 Self(i32::MIN)
135 } else {
136 Self(val as i32)
137 }
138 }
139
140 #[inline]
144 pub const fn to_i32_round_towards_nearest(self) -> i32 {
145 if self.0 >= 0 {
146 ((self.0 as i64 + (imul!() / 2)) / imul!()) as i32
147 } else {
148 ((self.0 as i64 - (imul!() / 2)) / imul!()) as i32
149 }
150 }
151
152 #[inline]
156 pub const fn to_i32_round_towards_zero(self) -> i32 {
157 (self.0 as i64 / imul!()) as i32
158 }
159
160 #[inline]
164 pub const fn to_i32_floor(self) -> i32 {
165 self.0 >> shift!()
166 }
167
168 #[inline]
172 pub const fn to_i32_ceil(self) -> i32 {
173 ((self.0 as i64 + imul!() - 1) >> shift!()) as i32
174 }
175}
176
177macro_rules! from {
178 ($t:ty) => {
179 impl From<$t> for Fixed {
180 #[inline]
181 fn from(value: $t) -> Self {
182 Self(value as i32 * imul!())
183 }
184 }
185 };
186}
187
188from!(i8);
189from!(u8);
190from!(i16);
191from!(u16);
192
193impl From<Fixed> for f64 {
194 #[inline]
195 fn from(value: Fixed) -> Self {
196 value.to_f64()
197 }
198}
199
200impl Debug for Fixed {
201 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
202 Debug::fmt(&self.to_f64(), f)
203 }
204}
205
206impl Display for Fixed {
207 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208 Display::fmt(&self.to_f64(), f)
209 }
210}
211
212macro_rules! forward_simple_immutable_binop {
213 ($slf:ty, $arg:ty, $big_name:ident, $small_name:ident, $op:tt) => {
214 impl $big_name<$arg> for $slf {
215 type Output = Fixed;
216
217 #[inline]
218 fn $small_name(self, rhs: $arg) -> Self::Output {
219 Fixed(self.0 $op rhs.0)
220 }
221 }
222 };
223}
224
225macro_rules! forward_simple_binop {
226 ($big_name:ident, $small_name:ident, $op:tt, $assign_big_name:ident, $assign_small_name:ident, $assign_op:tt) => {
227 forward_simple_immutable_binop!(Fixed, Fixed, $big_name, $small_name, $op);
228 forward_simple_immutable_binop!(Fixed, &Fixed, $big_name, $small_name, $op);
229 forward_simple_immutable_binop!(&Fixed, Fixed, $big_name, $small_name, $op);
230 forward_simple_immutable_binop!(&Fixed, &Fixed, $big_name, $small_name, $op);
231
232 impl $assign_big_name for Fixed {
233 #[inline]
234 fn $assign_small_name(&mut self, rhs: Self) {
235 self.0 $assign_op rhs.0;
236 }
237 }
238 };
239}
240
241forward_simple_binop!(Add, add, +, AddAssign, add_assign, +=);
242forward_simple_binop!(Sub, sub, -, SubAssign, sub_assign, -=);
243forward_simple_binop!(Rem, rem, %, RemAssign, rem_assign, %=);
244forward_simple_binop!(BitAnd, bitand, &, BitAndAssign, bitand_assign, &=);
245forward_simple_binop!(BitOr, bitor, |, BitOrAssign, bitor_assign, |=);
246forward_simple_binop!(BitXor, bitxor, ^, BitXorAssign, bitxor_assign, ^=);
247
248#[inline(always)]
249const fn mul(slf: i32, rhs: i32) -> i32 {
250 (slf as i64 * rhs as i64 / imul!()) as i32
251}
252
253#[inline(always)]
254const fn div(slf: i32, rhs: i32) -> i32 {
255 (slf as i64 * imul!() / rhs as i64) as i32
256}
257
258macro_rules! forward_complex_immutable_binop {
259 ($slf:ty, $arg:ty, $big_name:ident, $small_name:ident) => {
260 impl $big_name<$arg> for $slf {
261 type Output = Fixed;
262
263 #[inline]
264 fn $small_name(self, rhs: $arg) -> Self::Output {
265 Fixed($small_name(self.0, rhs.0))
266 }
267 }
268 };
269}
270
271macro_rules! forward_complex_binop {
272 ($big_name:ident, $small_name:ident, $assign_big_name:ident, $assign_small_name:ident) => {
273 forward_complex_immutable_binop!(Fixed, Fixed, $big_name, $small_name);
274 forward_complex_immutable_binop!(Fixed, &Fixed, $big_name, $small_name);
275 forward_complex_immutable_binop!(&Fixed, Fixed, $big_name, $small_name);
276 forward_complex_immutable_binop!(&Fixed, &Fixed, $big_name, $small_name);
277
278 impl $assign_big_name for Fixed {
279 #[inline]
280 fn $assign_small_name(&mut self, rhs: Self) {
281 self.0 = $small_name(self.0, rhs.0);
282 }
283 }
284 };
285}
286
287forward_complex_binop!(Mul, mul, MulAssign, mul_assign);
288forward_complex_binop!(Div, div, DivAssign, div_assign);
289
290macro_rules! forward_shiftop {
291 ($big_name:ident, $small_name:ident, $arg:ty, $op:tt, $assign_big_name:ident, $assign_small_name:ident, $assign_op:tt) => {
292 impl $big_name<$arg> for Fixed {
293 type Output = Fixed;
294
295 #[inline]
296 fn $small_name(self, rhs: $arg) -> Self::Output {
297 Fixed(self.0 $op rhs)
298 }
299 }
300
301 impl $big_name<$arg> for &Fixed {
302 type Output = Fixed;
303
304 #[inline]
305 fn $small_name(self, rhs: $arg) -> Self::Output {
306 Fixed(self.0 $op rhs)
307 }
308 }
309
310 impl $assign_big_name<$arg> for Fixed {
311 #[inline]
312 fn $assign_small_name(&mut self, rhs: $arg) {
313 self.0 $assign_op rhs;
314 }
315 }
316 };
317}
318
319macro_rules! forward_shift {
320 ($arg:ty) => {
321 forward_shiftop!(Shl, shl, $arg, <<, ShlAssign, shl_assign, <<=);
322 forward_shiftop!(Shl, shl, &$arg, <<, ShlAssign, shl_assign, <<=);
323 forward_shiftop!(Shr, shr, $arg, >>, ShrAssign, shr_assign, >>=);
324 forward_shiftop!(Shr, shr, &$arg, >>, ShrAssign, shr_assign, >>=);
325 }
326}
327
328forward_shift!(u8);
329forward_shift!(i8);
330forward_shift!(u16);
331forward_shift!(i16);
332forward_shift!(u32);
333forward_shift!(i32);
334forward_shift!(u64);
335forward_shift!(i64);
336forward_shift!(u128);
337forward_shift!(i128);
338forward_shift!(usize);
339forward_shift!(isize);
340
341macro_rules! forward_immutable_unop {
342 ($slf:ty, $big_name:ident, $small_name:ident, $op:tt) => {
343 impl $big_name for $slf {
344 type Output = Fixed;
345
346 #[inline]
347 fn $small_name(self) -> Self::Output {
348 Fixed($op self.0)
349 }
350 }
351 };
352}
353
354macro_rules! forward_unop {
355 ($big_name:ident, $small_name:ident, $op:tt) => {
356 forward_immutable_unop!(Fixed, $big_name, $small_name, $op);
357 forward_immutable_unop!(&Fixed, $big_name, $small_name, $op);
358 };
359}
360
361forward_unop!(Neg, neg, -);
362forward_unop!(Not, not, !);