Reputation: 29985
I have the following code:
typedef unsigned char uchar;
constexpr int leaing_ones(uchar const u8) noexcept {
return __builtin_clz(static_cast<uchar>(~static_cast<unsigned>(u8))) +
sizeof(uchar) * 8 - sizeof(int) * 8;
}
int main() {
static_assert(leaing_ones(0b0000'0000) == 0);
static_assert(leaing_ones(0b1000'0000) == 1);
static_assert(leaing_ones(0b1100'0000) == 2);
static_assert(leaing_ones(0b1110'0000) == 3);
static_assert(leaing_ones(0b1111'0000) == 4);
static_assert(leaing_ones(0b1111'1000) == 5);
static_assert(leaing_ones(0b1111'1000) == 5);
static_assert(leaing_ones(0b1111'1100) == 6);
static_assert(leaing_ones(0b1111'1110) == 7);
static_assert(leaing_ones(0b1111'1111) == 8);
}
I tested these with clang 12 and GCC 11.1. The only problem is that clang specifically doesn't like the last assertation:
<source>:16:17: error: static_assert expression is not an integral constant expression
static_assert(leaing_ones(0b1111'1111) == 8);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As far as I can tell, I am not causing any kind of overflow, and GCC works fine with all of them. Is this a clang bug, or am I overlooking something?
Upvotes: 0
Views: 3653
Reputation: 96699
Passing 0
to __builtin_clz
is undefined behavior.
From GCC manual:
Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
(bold mine)
Upvotes: 1
Reputation: 155516
__builtin_clz
is undefined for an argument with value zero, and since you invert all the bits, 0b1111'1111
becomes the pattern of all zeroes. Your function will need to special case 0
to use an appropriate value.
Upvotes: 1