div_int/
lib.rs

1//! Rational numbers with a compile-time denominator.
2//!
3//! This crate exports the [`DivInt`] struct, which is a wrapper around integers that are
4//! semantically divided by a compile-time constant. It's designed for embedded applications
5//! where floats are sometimes represented as rational numbers with a known denominator.
6//!
7//! # Example
8//!
9//! `DivInt<u8, 50>` is a number that's internally stored as a u8, but is semantically a rational
10//! number which value is the stored number divided by 50:
11//!
12//! ```
13//! use div_int::DivInt;
14//!
15//! let di: DivInt<u8, 50> = DivInt::from_numerator(15);
16//! assert_eq!(di.numerator(), 15);
17//! assert_eq!(di.to_f64(), 0.3);
18//! ```
19//!
20//! # Crate features
21//!
22//! The crate is `no_std` by default. Optional features are:
23//!
24//! * `serde` - adds serialization support. [Read more][`serde`].
25#![warn(missing_docs)]
26#![no_std]
27#![cfg_attr(docsrs, feature(doc_auto_cfg))]
28
29extern crate alloc;
30
31use core::fmt::{Debug, Formatter};
32
33#[cfg(feature = "serde")]
34pub mod serde;
35
36pub use div_int_procmacro::div_int;
37
38/// Rational number with a compile-time denominator.
39#[derive(Eq, PartialEq, Default, Ord, PartialOrd, Hash, Clone, Copy)]
40pub struct DivInt<N, const D: u64>(N);
41
42impl<N, const D: u64> DivInt<N, D> {
43    /// Constructs the type using the provided number as the numerator.
44    ///
45    /// The effective value of the result is therefore `D` times smaller than the provided number.
46    ///
47    /// Consider using the convenience macro [`div_int!`] instead.
48    pub const fn from_numerator(n: N) -> Self {
49        Self(n)
50    }
51}
52
53impl<N: FromF64Approx, const D: u64> DivInt<N, D> {
54    /// Constructs a `DivInt` by approximating a floating-point number.
55    ///
56    /// This function will return `None` if the provided number is outside the value range of the
57    /// `DivInt`.
58    ///
59    /// # Examples
60    /// ```
61    /// use div_int::{DivInt, div_int};
62    ///
63    /// assert_eq!(DivInt::<u8, 2>::from_f64_approx(1.5), Some(div_int!(3 / 2)));
64    /// assert_eq!(DivInt::<u8, 2>::from_f64_approx(128.0), None);
65    /// ```
66    pub fn from_f64_approx(val: f64) -> Option<Self> {
67        Some(Self::from_numerator(N::from_f64_approx(val * (D as f64))?))
68    }
69}
70
71impl<N: Into<f64>, const D: u64> From<DivInt<N, D>> for f64 {
72    fn from(value: DivInt<N, D>) -> Self {
73        value.0.into() / (D as f64)
74    }
75}
76
77impl<N: Copy + Into<f64>, const D: u64> DivInt<N, D> {
78    /// Floating-point value of this `DivInt`.
79    ///
80    /// # Examples
81    /// ```
82    /// use div_int::DivInt;
83    ///
84    /// assert_eq!(DivInt::<u16, 200>::from_numerator(150).to_f64(), 0.75);
85    /// ```
86    pub fn to_f64(self) -> f64 {
87        self.0.into() / (D as f64)
88    }
89}
90
91impl<N: Copy, const D: u64> DivInt<N, D> {
92    /// Numerator of this Ratio struct.
93    ///
94    /// # Examples
95    /// ```
96    /// use div_int::div_int;
97    ///
98    /// assert_eq!(div_int!(100 / 1024).numerator(), 100);
99    /// ```
100    pub const fn numerator(self) -> N {
101        self.0
102    }
103
104    /// Denominator of this `DivInt`.
105    ///
106    /// This is a convenience function, as this value can be extracted from the type itself.
107    ///
108    /// # Examples
109    /// ```
110    /// use div_int::div_int;
111    ///
112    /// assert_eq!(div_int!(100 / 1024).denominator(), 1024);
113    /// ```
114    pub const fn denominator(self) -> u64 {
115        D
116    }
117}
118
119impl<N: Debug, const D: u64> Debug for DivInt<N, D> {
120    /// Implements the `Debug` trait.
121    ///
122    /// # Example
123    /// ```
124    /// use div_int::DivInt;
125    ///
126    /// assert_eq!(format!("{:?}", DivInt::<_, 100>::from_numerator(5)), "div_int!(5 / 100)");
127    /// ```
128    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
129        f.write_str("div_int!(")?;
130        self.0.fmt(f)?;
131        f.write_str(" / ")?;
132        D.fmt(f)?;
133        f.write_str(")")
134    }
135}
136
137impl<N: Copy + Into<f64>, const D: u64> core::fmt::Display for DivInt<N, D> {
138    /// Implements the `Display` trait.
139    ///
140    /// # Example
141    /// ```
142    /// use div_int::div_int;
143    ///
144    /// assert_eq!(format!("{}", div_int!(10 / 50)), "0.2");
145    /// ```
146    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
147        core::fmt::Display::fmt(&self.to_f64(), f)
148    }
149}
150
151macro_rules! impl_core_op {
152    ($op:ident, $op_ident:ident) => {
153        impl<N: core::ops::$op, const D: u64> core::ops::$op for DivInt<N, D> {
154            type Output = DivInt<N::Output, D>;
155
156            fn $op_ident(self, rhs: Self) -> Self::Output {
157                DivInt::<N::Output, D>::from_numerator(self.0.$op_ident(rhs.0))
158            }
159        }
160    };
161}
162
163impl_core_op!(Add, add);
164impl_core_op!(Sub, sub);
165
166macro_rules! impl_core_assign_op {
167    ($op:ident, $op_ident:ident) => {
168        impl<N: core::ops::$op, const D: u64> core::ops::$op for DivInt<N, D> {
169            fn $op_ident(&mut self, rhs: Self) {
170                self.0.$op_ident(rhs.0);
171            }
172        }
173    };
174}
175
176impl_core_assign_op!(AddAssign, add_assign);
177impl_core_assign_op!(SubAssign, sub_assign);
178
179impl<N: core::ops::Neg, const D: u64> core::ops::Neg for DivInt<N, D> {
180    type Output = DivInt<N::Output, D>;
181
182    fn neg(self) -> Self::Output {
183        DivInt::<N::Output, D>::from_numerator(self.0.neg())
184    }
185}
186
187impl<N: core::ops::Shr, const D: u64> core::ops::Shr for DivInt<N, D> {
188    type Output = DivInt<N::Output, D>;
189
190    fn shr(self, rhs: Self) -> Self::Output {
191        DivInt::<N::Output, D>::from_numerator(self.0.shr(rhs.0))
192    }
193}
194
195impl<N: core::ops::Shl, const D: u64> core::ops::Shl for DivInt<N, D> {
196    type Output = DivInt<N::Output, D>;
197
198    fn shl(self, rhs: Self) -> Self::Output {
199        DivInt::<N::Output, D>::from_numerator(self.0.shl(rhs.0))
200    }
201}
202
203impl<N: num_traits::WrappingAdd, const D: u64> DivInt<N, D> {
204    /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the boundary of the type.
205    ///
206    /// # Examples
207    /// ```
208    /// use div_int::div_int;
209    ///
210    /// assert_eq!(div_int!(10u8 / 5).wrapping_add(div_int!(3u8 / 5)), div_int!(13u8 / 5));
211    /// assert_eq!(div_int!(10u8 / 5).wrapping_add(div_int!(250u8 / 5)), div_int!(4u8 / 5));
212    /// ```
213    pub fn wrapping_add(self, other: Self) -> Self {
214        Self(self.0.wrapping_add(&other.0))
215    }
216}
217
218impl<N: num_traits::WrappingSub, const D: u64> DivInt<N, D> {
219    /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the boundary of the type.
220    ///
221    /// # Examples
222    /// ```
223    /// use div_int::div_int;
224    ///
225    /// assert_eq!(div_int!(10u8 / 5).wrapping_sub(div_int!(3u8 / 5)), div_int!(7u8 / 5));
226    /// assert_eq!(div_int!(10u8 / 5).wrapping_sub(div_int!(20u8 / 5)), div_int!(246u8 / 5));
227    /// ```
228    pub fn wrapping_sub(self, other: Self) -> Self {
229        Self(self.0.wrapping_sub(&other.0))
230    }
231}
232
233impl<N: num_traits::WrappingNeg, const D: u64> DivInt<N, D> {
234    /// Wrapping negation. Computes `-self`, wrapping around at the boundary of the numerator type.
235    ///
236    /// # Examples
237    /// ```
238    /// use div_int::div_int;
239    ///
240    /// assert_eq!(div_int!(10i8 / 5).wrapping_neg(), div_int!(-10i8 / 5));
241    /// assert_eq!(div_int!(-128i8 / 5).wrapping_neg(), div_int!(-128i8 / 5));
242    /// ```
243    pub fn wrapping_neg(self) -> Self {
244        Self(self.0.wrapping_neg())
245    }
246}
247
248impl<N: num_traits::CheckedAdd, const D: u64> DivInt<N, D> {
249    /// Checked addition. Computes `self + rhs`, returning `None` if the result exceeds the boundary of the numerator type.
250    ///
251    /// # Examples
252    /// ```
253    /// use div_int::div_int;
254    ///
255    /// assert_eq!(div_int!(50u8 / 5).checked_add(div_int!(100u8 / _)), Some(div_int!(150u8 / _)));
256    /// assert_eq!(div_int!(50u8 / 5).checked_add(div_int!(250u8 / _)), None);
257    /// ```
258    pub fn checked_add(self, other: Self) -> Option<Self> {
259        self.0.checked_add(&other.0).map(Self)
260    }
261}
262
263impl<N: num_traits::CheckedSub, const D: u64> DivInt<N, D> {
264    /// Checked subtraction. Computes `self - rhs`, returning `None` if the result exceeds the boundary of the numerator type.
265    ///
266    /// # Examples
267    /// ```
268    /// use div_int::div_int;
269    ///
270    /// assert_eq!(div_int!(50u8 / 5).checked_sub(div_int!(40u8 / _)), Some(div_int!(10u8 / _)));
271    /// assert_eq!(div_int!(50u8 / 5).checked_sub(div_int!(60u8 / _)), None);
272    /// ```
273    pub fn checked_sub(self, other: Self) -> Option<Self> {
274        self.0.checked_sub(&other.0).map(Self)
275    }
276}
277
278impl<N: num_traits::CheckedNeg, const D: u64> DivInt<N, D> {
279    /// Checked negation. Computes `-self`, returning `None` if the result exceeds the boundary of the numerator type.
280    ///
281    /// # Examples
282    /// ```
283    /// use div_int::div_int;
284    ///
285    /// assert_eq!(div_int!(50u8 / 5).checked_neg(), None);
286    /// assert_eq!(div_int!(50i8 / 5).checked_neg(), Some(div_int!(-50i8 / 5)));
287    /// assert_eq!(div_int!(-128i8 / 5).checked_neg(), None);
288    /// assert_eq!(div_int!(127i8 / 5).checked_neg(), Some(div_int!(-127i8 / 5)) );
289    /// ```
290    pub fn checked_neg(self) -> Option<Self> {
291        self.0.checked_neg().map(Self)
292    }
293}
294
295impl<N: num_traits::FromBytes, const D: u64> DivInt<N, D> {
296    /// Creates a `DivInt` from its representation as a byte array in big endian.
297    ///
298    /// # Examples
299    /// ```
300    /// use div_int::{DivInt, div_int};
301    ///
302    /// assert_eq!(DivInt::<u16, 50>::from_be_bytes(&[1, 2]), div_int!(258u16 / 50));
303    /// ```
304    pub fn from_be_bytes(bytes: &N::Bytes) -> Self {
305        Self(N::from_be_bytes(bytes))
306    }
307
308    /// Creates a `DivInt` from its representation as a byte array in little endian.
309    ///
310    /// # Examples
311    /// ```
312    /// use div_int::{DivInt, div_int};
313    ///
314    /// assert_eq!(DivInt::<u16, 50>::from_le_bytes(&[1, 2]), div_int!(513u16 / 50));
315    /// ```
316    pub fn from_le_bytes(bytes: &N::Bytes) -> Self {
317        Self(N::from_le_bytes(bytes))
318    }
319
320    /// Creates a `DivInt` from its representation as a byte array in native endianness.
321    pub fn from_ne_bytes(bytes: &N::Bytes) -> Self {
322        Self(N::from_ne_bytes(bytes))
323    }
324}
325
326impl<N: num_traits::Signed, const D: u64> DivInt<N, D> {
327    /// Computes the absolute value of `self`.
328    ///
329    /// # Examples
330    /// ```
331    /// use div_int::div_int;
332    ///
333    /// assert_eq!(div_int!(5i8 / 50).abs(), div_int!(5i8 / 50));
334    /// assert_eq!(div_int!(-5i8 / 50).abs(), div_int!(5i8 / 50));
335    /// ```
336    pub fn abs(&self) -> Self {
337        Self(self.0.abs())
338    }
339
340    /// Returns `true` if `self` is positive and `false` if the numerator is zero or negative.
341    ///
342    /// # Examples
343    /// ```
344    /// use div_int::div_int;
345    ///
346    /// assert_eq!(div_int!(5i8 / 50).is_positive(), true);
347    /// assert_eq!(div_int!(-10i8 / 50).is_positive(), false);
348    /// ```
349    pub fn is_positive(&self) -> bool {
350        self.0.is_positive()
351    }
352
353    /// Returns `true` if `self` is negative and `false` if the numerator is zero or positive.
354    ///
355    /// # Examples
356    /// ```
357    /// use div_int::div_int;
358    ///
359    /// assert_eq!(div_int!(5i8 / 50).is_negative(), false);
360    /// assert_eq!(div_int!(-10i8 / 50).is_negative(), true);
361    /// ```
362    pub fn is_negative(&self) -> bool {
363        self.0.is_negative()
364    }
365}
366
367macro_rules! impl_num_op_wrapping_trait {
368    ($ty:ident, $op:ident) => {
369        impl<N: num_traits::$ty, const D: u64> num_traits::$ty for DivInt<N, D> {
370            fn $op(&self, v: &Self) -> Self {
371                Self(self.0.$op(&v.0))
372            }
373        }
374    }
375}
376
377impl_num_op_wrapping_trait!(WrappingAdd, wrapping_add);
378impl_num_op_wrapping_trait!(WrappingSub, wrapping_sub);
379
380impl<N: num_traits::WrappingNeg, const D: u64> num_traits::WrappingNeg for DivInt<N, D> {
381    fn wrapping_neg(&self) -> Self {
382        Self(self.0.wrapping_neg())
383    }
384}
385
386macro_rules! impl_num_op_checked_trait {
387    ($ty:ident, $op:ident) => {
388        impl<N: num_traits::$ty, const D: u64> num_traits::$ty for DivInt<N, D> {
389            fn $op(&self, v: &Self) -> Option<Self> {
390                self.0.$op(&v.0).map(Self)
391            }
392        }
393    }
394}
395
396impl_num_op_checked_trait!(CheckedAdd, checked_add);
397impl_num_op_checked_trait!(CheckedSub, checked_sub);
398
399impl<N: num_traits::CheckedNeg, const D: u64> num_traits::CheckedNeg for DivInt<N, D> {
400    fn checked_neg(&self) -> Option<Self> {
401        self.0.checked_neg().map(Self)
402    }
403}
404
405impl<N: num_traits::FromBytes, const D: u64> num_traits::FromBytes for DivInt<N, D> {
406    type Bytes = N::Bytes;
407
408    fn from_be_bytes(bytes: &Self::Bytes) -> Self {
409        Self(N::from_be_bytes(bytes))
410    }
411
412    fn from_le_bytes(bytes: &Self::Bytes) -> Self {
413        Self(N::from_le_bytes(bytes))
414    }
415}
416
417/// Helper trait for converting `f64` to integer types.
418pub trait FromF64Approx: Sized {
419    /// Constructs an integer type from a `f64`.
420    ///
421    /// Implementors must satisfy two invariants:
422    ///   * For input values in range of the output type, return the closest value.
423    ///   * For input values outside the range of the output type, return `None`.
424    fn from_f64_approx(v: f64) -> Option<Self>;
425}
426
427macro_rules! impl_fromf64_approx {
428    ($ty:ty, $fn_name:ident) => {
429        impl FromF64Approx for $ty {
430            fn from_f64_approx(v: f64) -> Option<Self> {
431                num_traits::ToPrimitive::$fn_name(&v)
432            }
433        }
434    };
435}
436
437impl_fromf64_approx!(u8, to_u8);
438impl_fromf64_approx!(u16, to_u16);
439impl_fromf64_approx!(u32, to_u32);
440impl_fromf64_approx!(u64, to_u64);
441impl_fromf64_approx!(i8, to_i8);
442impl_fromf64_approx!(i16, to_i16);
443impl_fromf64_approx!(i32, to_i32);
444impl_fromf64_approx!(i64, to_i64);
445impl_fromf64_approx!(f32, to_f32);
446impl_fromf64_approx!(f64, to_f64);
447
448/// A constructor for [`DivInt`] that infers the denominator.
449///
450/// In Rust 1.79, the following code does not compile (See [Rust issue #80528]):
451///
452/// ```ignore
453/// use div_int::DivInt;
454///
455/// let r: DivInt<u8, 50> = DivInt::<u8, _>::from_numerator(12);
456/// ```
457///
458/// This struct offers a workaround by using two separate generics for numerator and denominator:
459///
460/// ```
461/// use div_int::{InferredDenominator, DivInt};
462///
463/// let r: DivInt<u8, 50> = InferredDenominator::<u8>::div_int(12);
464/// ```
465///
466/// [Rust issue #80528]: https://github.com/rust-lang/rust/issues/80528
467pub struct InferredDenominator<N>(core::marker::PhantomData<N>);
468
469// https://github.com/rust-lang/rust/issues/80528
470impl<N> InferredDenominator<N> {
471    /// Constructs a [`DivInt`] instance.
472    ///
473    /// See the [struct-level documentation][InferredDenominator] for datils.
474    pub fn div_int<const D: u64>(numerator: N) -> DivInt<N, D> {
475        DivInt::from_numerator(numerator)
476    }
477}
478