【发布时间】:2017-07-14 16:28:45
【问题描述】:
我对以下代码的行为/输出感到困惑,要么这是一个错误,要么我遗漏了一些东西。 (天湖拱门上的 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;
}
这里是不同编译设置的不同输出。我知道如果通过零, clz 可能会返回未定义的结果。然而,直接内联调用总是可以正常工作,但是一旦涉及堆栈,编译器就会搞砸。
snk@maggy:~/HCS$ g++ -O0 test.cpp -o test
snk@maggy:~/HCS$ ./test
64 4196502
63 63
62 62
snk@maggy:~/HCS$
-O > 0 级别不会改变结果,我猜 gcc 是内联的。这是预期的结果...
snk@maggy:~/HCS$ g++ -O1 test.cpp -o test
snk@maggy:~/HCS$ ./test
64 64
63 63
62 62
使用 -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.
谢谢, 码
【问题讨论】:
-
您不应该期望未定义的行为会导致任何特定的结果(甚至两次相同的结果都不会)。这就是它未定义的原因。
-
未定义的结果或行为通常意味着整个程序不再可靠。
-
我怀疑在优化时,
__builtin_clzll(<constant>)正在编译时被评估 - 并试图提供一个合理的答案,尽管文档将结果描述为 undefined。没有异议,4196502的值虽然令人费解。 -
__builtin_clzll是 GCC 的优点,只处理非 0 值。 Intel 的LZCNT是 BMI 的一部分,可以处理 0 值。 -
你错过了一个要点。您没有阅读文档。 对于零结果是 UB。