Search code examples
c++c++11recursionbit-manipulationtemplate-meta-programming

Compile-time recursive function to compute the next power of two of an integer?


On the Bit Twiddling Hacks website the following algorithm is provided to round up an integer to the next power of two:

unsigned int v; // compute the next highest power of 2 of 32-bit v
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;

I would like to code a metaprogramming function that will compute the same operation:

  • recursively (for compile-time execution)
  • for any kind of integer (it should even work for possible awkward non-standard integers of any size like 15 bits, 65 bits...)

and here is the form of the expected function:

template <typename Type,
          // Something here (like a recursion index)
          class = typename std::enable_if<std::is_integral<Type>::value>::type,
          class = typename std::enable_if<std::is_unsigned<Type>::value>::type>
constexpr Type function(const Type value)
{
     // Something here
}

How to do that ?

Example: for value = 42 it should return 64


Solution

  • This ought to implement the algorithm you give:

    template<typename T>
    constexpr T roundup_helper( T value, unsigned maxb, unsigned curb ) {
        return maxb<=curb
                ? value
                : roundup_helper( ((value-1) | ((value-1)>>curb))+1, maxb, curb << 1 )
                ;
    }
    
    template<typename T,
            typename = typename enable_if<is_integral<T>::value>::type,
            typename = typename enable_if<is_unsigned<T>::value>::type>
    constexpr T roundup( T value ) {
        return roundup_helper( value, sizeof(T)*CHAR_BIT, 1 );
    }
    

    At least, it seems to work fine in my test program.

    Alternatively, you can move the v-1 and v+1 out of the helper function like so:

    template<typename T>
    constexpr T roundup_helper( T value, unsigned maxb, unsigned curb ) {
        return maxb<=curb
                ? value
                : roundup_helper( value | (value>>curb), maxb, curb << 1 )
                ;
    }
    
    template<typename T,
            typename = typename enable_if<is_integral<T>::value>::type,
            typename = typename enable_if<is_unsigned<T>::value>::type>
    constexpr T roundup( T value ) {
        return roundup_helper( value-1, sizeof(T)*CHAR_BIT, 1 )+1;
    }
    

    Another possibility is to take advantage of default arguments and put it all in a single function:

    template<typename T,
            typename = typename enable_if<is_integral<T>::value>::type,
            typename = typename enable_if<is_unsigned<T>::value>::type>
    constexpr T roundup(
            T value,
            unsigned maxb = sizeof(T)*CHAR_BIT,
            unsigned curb = 1
            ) {
        return maxb<=curb
                ? value
                : roundup( ((value-1) | ((value-1)>>curb))+1, maxb, curb << 1 )
                ;
    }