背景
长期以来,gcc一直提供了许多内置的位操作函数,特别是用于计算前导和末尾0位数的函数(同样适用于带有后缀l
和ll
的long unsigned
和long long unsigned
):
— 内置函数:
int __builtin_clz (unsigned int x)
返回在最高有效位开始处起,
x
中前导0位的个数。如果x
为0,则结果未定义。— 内置函数:
int __builtin_ctz (unsigned int x)
返回在最低有效位开始处起,
x
中末尾0位的个数。如果x
为0,则结果未定义。
然而,在我测试过的每个在线编译器上(免责声明:仅限x64),clz(0)
和ctz(0)
都返回底层内置类型的位数,例如
#include <iostream>
#include <limits>
int main()
{
// prints 32 32 32 on most systems
std::cout << std::numeric_limits<unsigned>::digits << " " << __builtin_ctz(0) << " " << __builtin_clz(0);
}
实时示例。
尝试的解决方法
在std=c++1y
模式下,最新的Clang SVN trunk已经将所有这些函数放宽到了C++14 constexpr
,这使它们成为了用于SFINAE表达式的候选包装函数模板的函数,这些表达式环绕着3个unsigned
、unsigned long
和unsigned long long
的ctz
/clz
内建函数。
template<class T> // wrapper class specialized for u, ul, ull (not shown)
constexpr int ctznz(T x) { return wrapper_class_around_builtin_ctz<T>()(x); }
// overload for platforms where ctznz returns size of underlying type
template<class T>
constexpr auto ctz(T x)
-> typename std::enable_if<ctznz(0) == std::numeric_limits<T>::digits, int>::type
{ return ctznz(x); }
// overload for platforms where ctznz does something else
template<class T>
constexpr auto ctz(T x)
-> typename std::enable_if<ctznz(0) != std::numeric_limits<T>::digits, int>::type
{ return x ? ctznz(x) : std::numeric_limits<T>::digits; }
这个技巧的好处在于,给出对于
ctz(0)
的要求的平台可以省略一个额外的条件测试x==0
(这可能看起来是微小的优化,但当你已经到了内置位操作函数的级别时,它可能会产生很大的差异)。
问题
clz(0)
和ctz(0)
的内置函数族有多么未定义?
- 它们会抛出
std::invalid_argument
异常吗? - 对于x64,当前gcc distro是否会返回底层类型的大小?
- ARM/x86平台有什么不同(我无法访问以测试)?
- 上述SFINAE技巧是区分这些平台的明确定义方法吗?
longlong.h
,请查找宏COUNT_LEADING_ZEROS_0... - Marc Glisse