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