c++gccbuilt-inbitset

Why does __builtin_clzll(variable) return a random number when the variable is 0, but returns 64 when the value is 0 directly?


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?
}

Issue:

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.


Solution

  • From gcc other builtins:

    int __builtin_clzll (unsigned long long):

    Similar to __builtin_clz, except the argument type is unsigned 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)