【问题标题】:Compile-time recursive function to compute the next power of two of an integer?编译时递归函数来计算整数的二的下一个幂?
【发布时间】:2014-02-13 10:01:01
【问题描述】:

Bit Twiddling Hacks website 上,提供了以下算法来将整数四舍五入到二的下一次幂:

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

【问题讨论】:

  • 是次高还是次高?它们是有区别的。一个是继任者,另一个是前任。只是好奇。
  • 他追求更高的东西,如代码所示
  • 你知道大多数编译器/机器组合都有一个 intlog 函数,它通常映射到一个操作码。
  • Danka 为样品。
  • 接下来,你关心编译时运行时效率吗? (我可以使用更糟糕的算法还是需要像上面那样聪明)

标签: c++ c++11 recursion bit-manipulation template-meta-programming


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    不幸的是,这可能不是您可以做的。但是,如果您有一个 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。

    如果没有这样的内在因素,我会回到亚当 H. 彼得森算法的路线上。但是这个的好处是它的简单性和效率。

    【讨论】:

      【解决方案3】:

      虽然总体上效率较低,但该算法可以相当简洁地完成工作:

      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 的最小幂 - 即 upperPowerOfTwo(1, 3) 返回 8。

      在大多数情况下效率较低的原因是它进行 O(sizeof(Type)*CHAR_BIT) 调用,而您链接的算法执行 O(log(sizeof(Type)*CHAR_BIT)) 操作。需要注意的是,该算法将在 log(v) 调用后终止,因此如果 v 足够小(即

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-23
        • 1970-01-01
        • 1970-01-01
        • 2017-11-10
        • 2017-03-19
        • 1970-01-01
        • 2015-03-04
        • 1970-01-01
        相关资源
        最近更新 更多