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}