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