Skip to main content

const_num_traits/ops/
rounding.rs

1//! Division-rounding and related operations: `div_ceil`, `div_floor`,
2//! exact division, multiple-of computations and overflow-free `midpoint`,
3//! mirroring the inherent methods on the primitive integer types.
4//!
5//! Stability in std (as of nightly 2026): `div_ceil` / `next_multiple_of`
6//! (unsigned) are stable since 1.73, `is_multiple_of` since 1.87, `midpoint`
7//! since 1.85/1.87; the signed `div_ceil` / `div_floor` /
8//! `next_multiple_of` and `div_exact` / `checked_div_exact` are still
9//! nightly-only. Anything newer than the crate's MSRV is hand-rolled here
10//! with the same algorithm as core.
11//!
12//! **CT tiers**: [`Midpoint`] is Tier A (branchless); everything else here
13//! is Tier C (division-based).
14
15use core::ops::{Add, Div, Rem};
16
17c0nst::c0nst! {
18/// Performs division, rounding the quotient towards positive infinity.
19pub c0nst trait DivCeil: Sized + [c0nst] Div<Self> {
20    /// Calculates the quotient of `self` and `rhs`, rounding the result
21    /// towards positive infinity.
22    ///
23    /// # Panics
24    ///
25    /// Panics if `rhs` is zero, or — with overflow checks enabled — on
26    /// `MIN / -1` for signed types.
27    ///
28    /// ```
29    /// use const_num_traits::DivCeil;
30    ///
31    /// assert_eq!(DivCeil::div_ceil(7u8, 2), 4);
32    /// assert_eq!(DivCeil::div_ceil(7i8, -2), -3);
33    /// assert_eq!(DivCeil::div_ceil(-7i8, 2), -3);
34    /// ```
35    fn div_ceil(self, rhs: Self) -> <Self as Div<Self>>::Output;
36}
37}
38
39macro_rules! div_ceil_impl {
40    (unsigned $($t:ty)*) => {$(
41        c0nst::c0nst! {
42        c0nst impl DivCeil for $t {
43            #[inline]
44            fn div_ceil(self, rhs: Self) -> Self {
45                <$t>::div_ceil(self, rhs)
46            }
47        }
48        }
49    )*};
50    // signed div_ceil is still unstable in std; same branchless algorithm
51    (signed $($t:ty)*) => {$(
52        c0nst::c0nst! {
53        c0nst impl DivCeil for $t {
54            #[inline]
55            fn div_ceil(self, rhs: Self) -> Self {
56                let d = self / rhs;
57                let r = self % rhs;
58                // `(self ^ rhs) >> (BITS - 1)` is -1 when the signs differ,
59                // 0 when they match; +1 only when rounding up is needed.
60                let correction = 1 + ((self ^ rhs) >> (<$t>::BITS - 1));
61                if r != 0 {
62                    d + correction
63                } else {
64                    d
65                }
66            }
67        }
68        }
69    )*};
70}
71
72div_ceil_impl!(unsigned usize u8 u16 u32 u64 u128);
73div_ceil_impl!(signed isize i8 i16 i32 i64 i128);
74
75c0nst::c0nst! {
76/// Performs division, rounding the quotient towards negative infinity.
77pub c0nst trait DivFloor: Sized + [c0nst] Div<Self> {
78    /// Calculates the quotient of `self` and `rhs`, rounding the result
79    /// towards negative infinity. For unsigned types this is the normal
80    /// integer division.
81    ///
82    /// # Panics
83    ///
84    /// Panics if `rhs` is zero, or — with overflow checks enabled — on
85    /// `MIN / -1` for signed types.
86    ///
87    /// ```
88    /// use const_num_traits::DivFloor;
89    ///
90    /// assert_eq!(DivFloor::div_floor(7u8, 2), 3);
91    /// assert_eq!(DivFloor::div_floor(7i8, -2), -4);
92    /// assert_eq!(DivFloor::div_floor(-7i8, 2), -4);
93    /// ```
94    fn div_floor(self, rhs: Self) -> <Self as Div<Self>>::Output;
95}
96}
97
98macro_rules! div_floor_impl {
99    // unsigned div_floor is plain division (and still unstable in std)
100    (unsigned $($t:ty)*) => {$(
101        c0nst::c0nst! {
102        c0nst impl DivFloor for $t {
103            #[inline]
104            fn div_floor(self, rhs: Self) -> Self {
105                self / rhs
106            }
107        }
108        }
109    )*};
110    (signed $($t:ty)*) => {$(
111        c0nst::c0nst! {
112        c0nst impl DivFloor for $t {
113            #[inline]
114            fn div_floor(self, rhs: Self) -> Self {
115                let d = self / rhs;
116                let r = self % rhs;
117                // all-ones (i.e. -1) iff the signs differ
118                let correction = (self ^ rhs) >> (<$t>::BITS - 1);
119                if r != 0 {
120                    d + correction
121                } else {
122                    d
123                }
124            }
125        }
126        }
127    )*};
128}
129
130div_floor_impl!(unsigned usize u8 u16 u32 u64 u128);
131div_floor_impl!(signed isize i8 i16 i32 i64 i128);
132
133c0nst::c0nst! {
134/// Performs division without remainder.
135pub c0nst trait DivExact: Sized + [c0nst] Div<Self> + [c0nst] Rem<Self> {
136    /// Integer division without remainder. Computes `self / rhs`, returning
137    /// `None` if `self % rhs != 0`.
138    ///
139    /// # Panics
140    ///
141    /// Panics if `rhs` is zero, or — with overflow checks enabled — on
142    /// `MIN / -1` for signed types.
143    ///
144    /// ```
145    /// use const_num_traits::DivExact;
146    ///
147    /// assert_eq!(DivExact::div_exact(64u8, 2), Some(32));
148    /// assert_eq!(DivExact::div_exact(65u8, 2), None);
149    /// ```
150    fn div_exact(self, rhs: Self) -> Option<<Self as Div<Self>>::Output>;
151
152    /// Checked integer division without remainder. Computes `self / rhs`,
153    /// returning `None` if `rhs == 0`, `self % rhs != 0`, or the division
154    /// overflowed (`MIN / -1` on a signed type).
155    fn checked_div_exact(self, rhs: Self) -> Option<<Self as Div<Self>>::Output>;
156}
157}
158
159macro_rules! div_exact_impl {
160    ($($t:ty)*) => {$(
161        c0nst::c0nst! {
162        c0nst impl DivExact for $t {
163            #[inline]
164            fn div_exact(self, rhs: Self) -> Option<Self> {
165                if self % rhs != 0 {
166                    None
167                } else {
168                    Some(self / rhs)
169                }
170            }
171
172            #[inline]
173            fn checked_div_exact(self, rhs: Self) -> Option<Self> {
174                // checked_rem rejects rhs == 0 and the MIN % -1 overflow,
175                // checked_div rejects MIN / -1; together they cover all the
176                // None cases without panicking.
177                match <$t>::checked_rem(self, rhs) {
178                    None => None,
179                    Some(r) => {
180                        if r != 0 {
181                            None
182                        } else {
183                            <$t>::checked_div(self, rhs)
184                        }
185                    }
186                }
187            }
188        }
189        }
190    )*};
191}
192
193div_exact_impl!(usize u8 u16 u32 u64 u128);
194div_exact_impl!(isize i8 i16 i32 i64 i128);
195
196c0nst::c0nst! {
197/// Checks divisibility, mirroring `is_multiple_of` on the unsigned primitives.
198pub c0nst trait MultipleOf: Sized + [c0nst] Rem<Self> {
199    /// Returns `true` if `self` is an integer multiple of `rhs`, and `false`
200    /// otherwise.
201    ///
202    /// This function is equivalent to `self % rhs == 0`, except that it
203    /// will not panic for `rhs == 0`: instead, `0.is_multiple_of(0) == true`
204    /// and `x.is_multiple_of(0) == false` for any nonzero `x`.
205    ///
206    /// Like std, this is only provided for unsigned types.
207    ///
208    /// ```
209    /// use const_num_traits::MultipleOf;
210    ///
211    /// assert!(MultipleOf::is_multiple_of(6u8, 2));
212    /// assert!(!MultipleOf::is_multiple_of(5u8, 2));
213    /// assert!(MultipleOf::is_multiple_of(0u8, 0));
214    /// assert!(!MultipleOf::is_multiple_of(5u8, 0));
215    /// ```
216    fn is_multiple_of(self, rhs: Self) -> bool;
217}
218}
219
220macro_rules! multiple_of_impl {
221    ($($t:ty)*) => {$(
222        c0nst::c0nst! {
223        c0nst impl MultipleOf for $t {
224            #[inline]
225            fn is_multiple_of(self, rhs: Self) -> bool {
226                match rhs {
227                    0 => self == 0,
228                    _ => self % rhs == 0,
229                }
230            }
231        }
232        }
233    )*};
234}
235
236multiple_of_impl!(usize u8 u16 u32 u64 u128);
237
238c0nst::c0nst! {
239/// Rounds up to the nearest multiple of a value.
240pub c0nst trait NextMultipleOf: Sized + [c0nst] Add<Self> + [c0nst] Rem<Self> {
241    /// Calculates the smallest value greater than or equal to `self` that is
242    /// a multiple of `rhs` (for negative `rhs` on signed types: the closest
243    /// to negative infinity).
244    ///
245    /// # Panics
246    ///
247    /// Panics if `rhs` is zero, or — with overflow checks enabled — if the
248    /// result overflows.
249    ///
250    /// ```
251    /// use const_num_traits::NextMultipleOf;
252    ///
253    /// assert_eq!(NextMultipleOf::next_multiple_of(16u8, 8), 16);
254    /// assert_eq!(NextMultipleOf::next_multiple_of(23u8, 8), 24);
255    /// assert_eq!(NextMultipleOf::next_multiple_of(-16i8, -5), -20);
256    /// ```
257    fn next_multiple_of(self, rhs: Self) -> <Self as Add<Self>>::Output;
258
259    /// Calculates the smallest value greater than or equal to `self` that is
260    /// a multiple of `rhs`, returning `None` if `rhs` is zero or the
261    /// operation would result in overflow.
262    fn checked_next_multiple_of(self, rhs: Self) -> Option<<Self as Add<Self>>::Output>;
263}
264}
265
266macro_rules! next_multiple_of_impl {
267    (unsigned $($t:ty)*) => {$(
268        c0nst::c0nst! {
269        c0nst impl NextMultipleOf for $t {
270            #[inline]
271            fn next_multiple_of(self, rhs: Self) -> Self {
272                <$t>::next_multiple_of(self, rhs)
273            }
274
275            #[inline]
276            fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
277                <$t>::checked_next_multiple_of(self, rhs)
278            }
279        }
280        }
281    )*};
282    // signed next_multiple_of is still unstable in std; same algorithm
283    (signed $($t:ty)*) => {$(
284        c0nst::c0nst! {
285        c0nst impl NextMultipleOf for $t {
286            #[inline]
287            fn next_multiple_of(self, rhs: Self) -> Self {
288                // rhs == -1 would otherwise fail computing `MIN % -1`
289                if rhs == -1 {
290                    return self;
291                }
292                let r = self % rhs;
293                let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) {
294                    r + rhs
295                } else {
296                    r
297                };
298                if m == 0 {
299                    self
300                } else {
301                    self + (rhs - m)
302                }
303            }
304
305            #[inline]
306            fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
307                if rhs == -1 {
308                    return Some(self);
309                }
310                let r = match <$t>::checked_rem(self, rhs) {
311                    Some(r) => r,
312                    None => return None,
313                };
314                let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) {
315                    // r + rhs cannot overflow: opposite signs
316                    r + rhs
317                } else {
318                    r
319                };
320                if m == 0 {
321                    Some(self)
322                } else {
323                    // rhs - m cannot overflow: m has the same sign as rhs
324                    <$t>::checked_add(self, rhs - m)
325                }
326            }
327        }
328        }
329    )*};
330}
331
332next_multiple_of_impl!(unsigned usize u8 u16 u32 u64 u128);
333next_multiple_of_impl!(signed isize i8 i16 i32 i64 i128);
334
335c0nst::c0nst! {
336/// Computes the midpoint (average) of two values without overflowing.
337pub c0nst trait Midpoint: Sized {
338    /// Calculates the midpoint `(self + rhs) / 2` as if it were performed in
339    /// a sufficiently-large type, so it never overflows. The result is
340    /// rounded towards zero.
341    ///
342    /// ```
343    /// use const_num_traits::Midpoint;
344    ///
345    /// assert_eq!(Midpoint::midpoint(u8::MAX, u8::MAX), u8::MAX);
346    /// assert_eq!(Midpoint::midpoint(0u8, 7), 3);
347    /// assert_eq!(Midpoint::midpoint(-7i8, 0), -3);
348    /// ```
349    type Output;
350    fn midpoint(self, rhs: Self) -> Self::Output;
351}
352}
353
354macro_rules! midpoint_impl {
355    (unsigned $($t:ty)*) => {$(
356        c0nst::c0nst! {
357        c0nst impl Midpoint for $t {
358            type Output = $t;
359            #[inline]
360            fn midpoint(self, rhs: Self) -> Self {
361                <$t>::midpoint(self, rhs)
362            }
363        }
364        }
365    )*};
366    // signed midpoint is stable since 1.87, newer than the MSRV; same
367    // branchless Hacker's Delight algorithm as core
368    (signed $($t:ty)*) => {$(
369        c0nst::c0nst! {
370        c0nst impl Midpoint for $t {
371            type Output = $t;
372            #[inline]
373            fn midpoint(self, rhs: Self) -> Self {
374                let t = ((self ^ rhs) >> 1) + (self & rhs);
375                // The floor average of two integers whose sum is an odd
376                // negative number is one below their truncated average;
377                // bump it back towards zero.
378                t + (if t < 0 { 1 } else { 0 } & (self ^ rhs))
379            }
380        }
381        }
382    )*};
383}
384
385midpoint_impl!(unsigned usize u8 u16 u32 u64 u128);
386midpoint_impl!(signed isize i8 i16 i32 i64 i128);
387
388#[cfg(test)]
389mod tests {
390    use super::*;
391
392    #[test]
393    fn div_ceil_floor() {
394        // cross-check the signed hand-rolled bodies against all small values
395        for a in -50i32..=50 {
396            for b in -50i32..=50 {
397                if b == 0 {
398                    continue;
399                }
400                let ceil = DivCeil::div_ceil(a, b);
401                let floor = DivFloor::div_floor(a, b);
402                let exact = (a as f64) / (b as f64);
403                assert_eq!(ceil, exact.ceil() as i32, "{a} div_ceil {b}");
404                assert_eq!(floor, exact.floor() as i32, "{a} div_floor {b}");
405            }
406        }
407        assert_eq!(DivCeil::div_ceil(7u8, 2), 4);
408        assert_eq!(DivFloor::div_floor(7u8, 2), 3);
409    }
410
411    #[test]
412    fn div_exact() {
413        assert_eq!(DivExact::div_exact(64u8, 4), Some(16));
414        assert_eq!(DivExact::div_exact(66u8, 4), None);
415        assert_eq!(DivExact::checked_div_exact(64u8, 0), None);
416        assert_eq!(DivExact::checked_div_exact(i8::MIN, -1), None);
417        assert_eq!(DivExact::checked_div_exact(-64i8, -4), Some(16));
418        assert_eq!(DivExact::checked_div_exact(-65i8, -4), None);
419    }
420
421    #[test]
422    fn multiples() {
423        assert!(MultipleOf::is_multiple_of(12u32, 4));
424        assert!(!MultipleOf::is_multiple_of(12u32, 5));
425        assert!(MultipleOf::is_multiple_of(0u32, 0));
426        assert!(!MultipleOf::is_multiple_of(12u32, 0));
427
428        assert_eq!(NextMultipleOf::next_multiple_of(23u8, 8), 24);
429        assert_eq!(NextMultipleOf::checked_next_multiple_of(250u8, 8), None);
430        assert_eq!(NextMultipleOf::checked_next_multiple_of(23u8, 0), None);
431        // match std's signed semantics
432        assert_eq!(NextMultipleOf::next_multiple_of(16i8, 8), 16);
433        assert_eq!(NextMultipleOf::next_multiple_of(23i8, 8), 24);
434        assert_eq!(NextMultipleOf::next_multiple_of(16i8, -8), 16);
435        assert_eq!(NextMultipleOf::next_multiple_of(23i8, -8), 16);
436        assert_eq!(NextMultipleOf::next_multiple_of(-16i8, 8), -16);
437        assert_eq!(NextMultipleOf::next_multiple_of(-23i8, 8), -16);
438        assert_eq!(NextMultipleOf::next_multiple_of(-16i8, -8), -16);
439        assert_eq!(NextMultipleOf::next_multiple_of(-23i8, -8), -24);
440        assert_eq!(NextMultipleOf::next_multiple_of(i8::MIN, -1), i8::MIN);
441        assert_eq!(NextMultipleOf::checked_next_multiple_of(i8::MAX, 2), None);
442    }
443
444    #[test]
445    fn midpoint() {
446        assert_eq!(Midpoint::midpoint(u8::MAX, u8::MAX), u8::MAX);
447        assert_eq!(Midpoint::midpoint(0u8, 1), 0);
448        // signed: rounded towards zero, matching std's documented examples
449        assert_eq!(Midpoint::midpoint(-1i8, 2), 0);
450        assert_eq!(Midpoint::midpoint(-7i8, 0), -3);
451        assert_eq!(Midpoint::midpoint(0i8, 7), 3);
452        assert_eq!(Midpoint::midpoint(i8::MIN, i8::MAX), 0);
453        assert_eq!(Midpoint::midpoint(i8::MIN, i8::MIN), i8::MIN);
454    }
455}