Bit-Manipulation

210. next_power_of_two — Round Up to a Power of Two Without the Bit-Twiddling

Sizing a buffer or hash table to the next power of two is a classic — and people keep reinventing it with shifts and leading_zeros. The standard library already has it.

You’ve probably seen (or written) the bit-twiddling version, which is easy to get subtly wrong on edge cases like 0 or exact powers:

1
2
3
4
5
6
7
8
9
fn round_up_manual(n: u32) -> u32 {
    let mut v = n - 1;        // underflows when n == 0
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v + 1
}

next_power_of_two does exactly this, correctly, for every unsigned integer type:

1
2
3
4
assert_eq!(5u32.next_power_of_two(), 8);
assert_eq!(8u32.next_power_of_two(), 8);   // already a power of two
assert_eq!(1u32.next_power_of_two(), 1);
assert_eq!(0u32.next_power_of_two(), 1);   // the tricky one, handled

A common use is rounding an allocation up so masking can replace modulo:

1
2
3
4
5
6
7
let requested = 100usize;
let cap = requested.next_power_of_two();   // 128
assert_eq!(cap, 128);

// because cap is a power of two, idx % cap == idx & (cap - 1)
let idx = 1234usize;
assert_eq!(idx % cap, idx & (cap - 1));

The one trap: if the next power of two doesn’t fit in the type, next_power_of_two panics in debug and wraps to 0 in release. When the input is untrusted, reach for checked_next_power_of_two, which hands you an Option instead:

1
2
3
assert_eq!(200u8.next_power_of_two(), 0);          // wraps in release — bug waiting to happen
assert_eq!(200u8.checked_next_power_of_two(), None); // explicit, safe
assert_eq!(100u8.checked_next_power_of_two(), Some(128));