【发布时间】:2013-10-31 21:58:03
【问题描述】:
背景
长期以来,gcc has been providing 内置了许多位旋转函数,特别是尾随和前导 0 位的数量(也适用于 long unsigned 和 long long unsigned,它们有后缀 l 和 @ 987654326@):
——内置函数:
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 主干使所有这些函数都放松了 C++14 constexpr,这使得它们可以在 SFINAE 表达式中用于围绕 3 ctz / @ 的包装函数模板987654339@ 内置 unsigned、unsigned long 和 unsigned long long
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; }
这个 hack 的好处是,为 ctz(0) 提供所需结果的平台可以省略额外的条件来测试 x==0(这似乎是一种微优化,但当您已经降到内置的bit-twiddling函数,它可以产生很大的不同)
问题
内置函数系列clz(0) 和ctz(0) 的未定义程度如何?
- 他们可以抛出
std::invalid_argument异常吗? - 对于 x64,对于当前的 gcc 发行版,它们会返回底层类型的大小吗?
- ARM/x86 平台有什么不同吗(我无权测试这些平台)?
- 上述 SFINAE 技巧是一种明确定义的分离此类平台的方法吗?
【问题讨论】:
-
如果您可以在 gcc/gmp/glibc 中找到文件
longlong.h,请查找宏 COUNT_LEADING_ZEROS_0...
标签: c++ undefined-behavior constexpr c++14 bit-manipulation