[英]Compile-time recursive function to compute the next power of two of an integer?
在Bit Twiddling Hacks网站上 ,提供了以下算法,可将整数四舍五入到下一个2的幂:
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++;
我想编写一个将计算相同操作的元编程函数:
这是预期函数的形式:
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-1
和v+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。
如果没有这种内在因素,我会回到亚当·H·彼得森算法的基础上。 但是,这一点的好处是它的简单性和效率。
尽管一般而言效率较低,但此算法将相当简洁地完成工作:
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足够小(即<log(v型的最大值)),它将更快。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.