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