首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >__builtin_ctz(0)或__builtin_clz(0)有多难定义?

__builtin_ctz(0)或__builtin_clz(0)有多难定义?
EN

Stack Overflow用户
提问于 2013-10-23 04:40:47
回答 2查看 18.6K关注 0票数 25

背景

在很长一段时间内,gcc has been providing许多内置的位闲置函数,特别是尾随和前导0位的数量(也适用于long unsignedlong long unsigned,它们的后缀分别为lll):

内置函数: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)都返回了底层内置类型的位数,例如

代码语言:javascript
复制
#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,这使得它们可以在unsignedunsigned longunsigned long long的3个ctz / clz内置函数周围的包装函数模板的SFINAE表达式中使用

代码语言:javascript
复制
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 std::invalid_argument x64吗?对于当前的gcc发行版,他们会返回底层类型的大小吗?
  • ARM/x86平台有什么不同(我无法访问它来测试这些平台)?
  • 上面的SFINAE技巧是一种明确定义的分离此类平台的方式吗?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-10-23 05:43:56

不幸的是,即使是x86-64实现也可能有所不同--与英特尔的instruction set referenceBSFBSR不同,源操作数值为(0)的x86-64实现没有定义目标,并设置了ZF (零标志)。因此,微体系结构或AMD和Intel之间的行为可能并不一致。(我相信AMD保持目的地不变。)

较新的LZCNTTZCNT指令并不普遍。两者都只存在于Haswell架构(针对英特尔)。

票数 15
EN

Stack Overflow用户

发布于 2014-11-08 04:01:18

该值未定义的原因是,它允许编译器使用结果未定义的处理器指令,而这些指令是获得答案的最快方法。

但重要的是要理解,不仅结果是不确定的,它们也是不确定的。例如,根据英特尔的指令参考,该指令返回当前时间的低7位是有效的。

这就是它变得有趣/危险的地方:编译器编写者可以利用这种情况来生成较小的代码。考虑以下代码的非模板专门化版本:

代码语言:javascript
复制
using std::numeric_limits;
template<class T>
constexpr auto ctz(T x) {
  return ctznz(0) == numeric_limits<T>::digits || x != 0
       ? ctznz(x) : numeric_limits<T>::digits;
}

这在决定为ctznz(0)返回#位的处理器/编译器上工作得很好。但在决定返回伪随机值的处理器/编译器上,编译器可能会决定“我可以为ctznz(0)返回任何我想要的东西,如果我返回#bit,代码就会更小,所以我会这样做”。然后代码就会一直调用ctznz,即使它产生了错误的答案。

换句话说:编译器的未定义结果不一定是未定义的,就像正在运行的程序的未定义结果一样。

确实没有办法绕过这一点。如果必须使用源操作数可能为零的__builtin_clz,则必须始终添加检查。

票数 17
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19527897

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档