I’m trying to understand how the __builtin_clzll
function works, but I’m encountering unexpected behavior when using a variable instead of a direct 0. Here is a minimal example:
#include <iostream>
using namespace std;
int main() {
unsigned long long x = 0;
cout << __builtin_clzll(x) << endl; // Why does this output a random number, like 119030102.
cout << __builtin_clzll(0) << endl; // Why does this output 64?
}
Why does this discrepancy occur when using a variable instead of the constant 0? Is this behavior expected? Could it be related to the way the variable is being handled?
What I tried: I tried to use the __builtin_clzll function with a variable that is set to 0.
What I expected: I expected it to return 64, as 0 has 64 leading zeros.
What happened: However, the function returned a random number instead of 64.
From gcc other builtins:
int __builtin_clzll (unsigned long long)
:
Similar to
__builtin_clz
, except the argument type isunsigned long long
.
int __builtin_clz (unsigned int x)
:
Returns the number of leading 0-bits in x, starting at the most significant bit position. If
x
is 0, the result is undefined.
So, with x
being 0, the result is undefined. Don't call it with 0 - or use the standard function std::countl_zero
which does the zero-check for you.
If you can't use C++20 or later, gcc also has another version:
int __builtin_clzg (...)
:
Similar to
__builtin_clz
, except the argument is type-generic unsigned integer (standard, extended or bit-precise) and there is optional second argument with int type. No integral argument promotions are performed on the first argument. If two arguments are specified, and first argument is 0, the result is the second argument. If only one argument is specified and it is 0, the result is undefined.
Which means you could call it like this to get the result you expected:
__builtin_clzg(x, std::numeric_limits<decltype(x)>::digits)