编译时间递归函数以计算两个整数的下一个功能

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

本文关键字:两个 整数 功能 下一个 递归函数 时间 计算 编译      更新时间:2023-10-16

在BIT TWIDDLING HACKS网站上,提供了以下算法来将整数汇总到下一个功能:

的下一个功能
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++;

我想编码一个将计算相同操作的元编程功能:

  • 递归(用于编译时执行)
  • 对于任何类型的整数(甚至应该适用于任何大小(如15位,65位)的可能尴尬的非标准整数...)

这是预期函数的形式:

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
}

如何做?

示例:对于value = 42,它应该返回64

这应该实现您给出的算法:

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 );
}

至少,它在我的测试程序中似乎很好。

另外,您可以将v-1v+1移出助手功能,例如:

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;
}

另一种可能性是利用默认参数并将其全部放在一个函数中:

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 )
            ;
}

这可能不是您不幸的事情。但是,如果您有任何偶然的constexpr计数领导零编译器的固有,则以下既有编译时都非常有效,并且在运行时,如果您碰巧给它进行运行时间参数:

#include <climits>
template <class Int>
inline
constexpr
Int
clp2(Int v)
{
    return v > 1 ? 1 << (sizeof(Int)*CHAR_BIT - __builtin_clz(v-1)) : v;
}
int
main()
{
    static_assert(clp2(0) == 0, "");
    static_assert(clp2(1) == 1, "");
    static_assert(clp2(2) == 2, "");
    static_assert(clp2(3) == 4, "");
    static_assert(clp2(4) == 4, "");
    static_assert(clp2(5) == 8, "");
    static_assert(clp2(6) == 8, "");
    static_assert(clp2(7) == 8, "");
    static_assert(clp2(8) == 8, "");
    static_assert(clp2(42) == 64, "");
}

我用灌输曲折的叮当声编译了上面的内容。它并非没有问题。您需要决定要对负面论据做什么。但是许多架构和编译器都具有这样的内在性(可耻的是现在不是标准的C/C )。其中一些可能会成为固有的constexpr。

没有这样内在的,我会依靠亚当·彼得森(Adam H. Peterson)的算法。但是,这是它的简单性和效率。

尽管一般效率较低,但该算法将很简单地完成工作:

template <typename T,
          typename = typename std::enable_if<std::is_integral<T>::value>::type,
          typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
constexpr T upperPowerOfTwo(T value, size_t pow = 0)
{
    return (value >> pow) ? upperPowerOfTwo(value, pow + 1)
                          : T(1) << pow;
}

这还允许您指定2 -imimum功率2 -i. upperPowerOfTwo(1, 3)返回8.

在大多数情况下,这效率较低的原因是它使O(sizeof(type)*char_bit)调用,而您链接的算法执行O(log(sizeof(type)*char_bit))操作。警告是该算法将在log(v)调用后终止,因此,如果V足够小(即&lt; log(v-type的最大值)),则它将更快。