我对以下代码的行为/输出感到困惑,要么这是一个错误,要么我遗漏了一些东西。(天湖拱门上的 Ubuntu 16.04)
#include <iostream>
int wrap(unsigned long long val) {
return __builtin_clzll(val);
}
using namespace std;
int main() {
cout << __builtin_clzll(0) << " " << wrap(0) << endl;
cout << __builtin_clzll(1) << " " << wrap(1) << endl;
cout << __builtin_clzll(2) << " " << wrap(2) << endl;
}
Run Code Online (Sandbox Code Playgroud)
这是不同编译设置下的不同输出。我知道如果传递零, clz 可能会返回未定义的结果。然而,直接内联调用总是可以正常工作,但是一旦涉及到堆栈,编译器就会搞砸。
snk@maggy:~/HCS$ g++ -O0 test.cpp -o test
snk@maggy:~/HCS$ ./test
64 4196502
63 63
62 62
snk@maggy:~/HCS$
Run Code Online (Sandbox Code Playgroud)
-O > 0 级别不会改变结果,我猜 gcc 是内联的。这是预期的结果……
snk@maggy:~/HCS$ g++ -O1 test.cpp -o test
snk@maggy:~/HCS$ ./test
64 64
63 63
62 62
Run Code Online (Sandbox Code Playgroud)
使用 -mlzcnt 效果会更好:
snk@maggy:~/HCS$ g++ -O0 -mlzcnt test.cpp -o test
snk@maggy:~/HCS$ ./test
64 0
63 0
62 1
snk@maggy:~/HCS$ g++ -O1 -mlzcnt test.cpp -o test
snk@maggy:~/HCS$ ./test
64 64
63 63
62 62
snk@maggy:~/HCS$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Run Code Online (Sandbox Code Playgroud)
谢谢,Ch
这个问题中有趣的例子是 的行为-mlzcnt。这在 2013 年被报告为GCC bug 58928,但该错误报告后来被撤回,因为当您-mlzcnt为不支持该LZCNT操作码的 Intel CPU 提供时,这是“预期”行为。
事实证明,LZCNT是一个BSR带有前缀的(Bit Search Reverse)F3;在未实现 LZCNT 的 Intel CPU 上,它不会被捕获为无效操作码,而是被解释为 BSR,它返回 1 位的位位置(位 0 为低位),而不是前面 0 的数量。
如前所述,__builtin_clz使用参数 0 调用会产生未定义的行为。您不应该对未定义行为的结果抱有任何期望;甚至两次都会得到相同的结果。
根据内置函数的 GCC 文档(添加粗体文本)
内置函数:int __builtin_clz ( unsigned int x )
返回x中前导 0 位的数量(从最高有效位位置开始)。如果x为 0,则结果未定义。
...
内置函数:int __builtin_clzll ( unsigned long long )
与 类似
__builtin_clz,但参数类型为unsigned long long。
的结果0是未定义的。