patterncppMinor
Bit manipulation tool set
Viewed 0 times
bitmanipulationsettool
Problem
I made a tool set to facilitate bit manipulation as follows. Any comments are welcome, be it about the interface design, the implementation, or even the naming. Also, I want to make sure that the tool set is not lacking something for use-cases I failed to catch :-) Original code and unit tests can be found here and here.
/// The index is zero-based, starting from the least significant bit.
/// The most significant bit or sign bit is the head bit.
template
constexpr unsigned num_bits(T = 0) {
return sizeof(T)
constexpr unsigned head_bit_idx(T = 0) {
return num_bits() - 1;
}
template
T bit_flag(unsigned idx) {
// since C++14, `1 - 1)` is well-defined and makes `INT_MIN`
return static_cast(1)
constexpr T head_bit_flag() {
return static_cast(1) ();
}
template
bool test_bit(T x, unsigned idx) {
return x & bit_flag(idx);
}
template
bool test_head_bit(T x) {
return test_bit(x, head_bit_idx(x));
}
template
T set_bit(T x, unsigned idx) {
return x | bit_flag(idx);
}
template
T set_head_bit(T x) {
return set_bit(x, head_bit_idx());
}
template
T clear_bit(T x, unsigned idx) {
return x & ~bit_flag(idx);
}
template
T clear_head_bit(T x) {
return clear_bit(x, head_bit_idx());
}
template
T flip_bit(T x, unsigned idx) {
return x ^ bit_flag(idx);
}
template
T flip_head_bit(T x) {
return flip_bit(x, head_bit_idx());
}
template
unsigned num_bits_set(T x) {
unsigned cnt = 0;
// behavior of signed type underflow is unspecified
std::make_unsigned_t v = x;
while (v) {
++cnt;
v &= v - 1;
}
return cnt;
}Solution
False optimization (and makes it hard to read).
This should be
Also its not actually 8. TO be compliant and accurate use
This is a bit inefficient.
You are using a runtime loop (over each bit). You could use a compile time loop over each byte. Because the number of bits set for each specific value for a byte it is easy to pre-calculate.
Note: For each byte there are only
Overall its fine. But you can get the same functionality from using
template
constexpr unsigned num_bits(T = 0) {
return sizeof(T) << 3;
}This should be
sizeof(T) * 8 that's so much easier to read. And if it quicker to shift that is what the compiler will actually do. Don't try and outsmart the compiler the only thing that will is happen is that you will outsmart yourself.Also its not actually 8. TO be compliant and accurate use
CHAR_BIT. This is a bit inefficient.
template
unsigned num_bits_set(T x) {
unsigned cnt = 0;
// behavior of signed type underflow is unspecified
std::make_unsigned_t v = x;
while (v) {
++cnt;
v &= v - 1;
}
return cnt;
}You are using a runtime loop (over each bit). You could use a compile time loop over each byte. Because the number of bits set for each specific value for a byte it is easy to pre-calculate.
Note: For each byte there are only
CHAR_BIT number of bits (usually 8). Which is 256 values. So you can swap space for time here for a single byte.unsigned num_bits_set_count_value[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 000-015
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 016-031
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 032-047
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 048-063
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 064-079
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 080-095
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 096-111
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 112-127
// ---
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 128-143
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 144-159
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 160-175
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 176-191
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 192-207
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 208-223
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 224-239
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; // 240-255
// Use compile time meta program
// to unwind a multi byte value into a set of adds
// these will all be inlined.
template
unsigned num_bits_set_count(char const* byteStream) {
return num_bits_set_count_value[*byteStream]
+ num_bits_set_count(byteStream + 1);
}
template<> unsigned num_bits_set_count(char const*) {return 0;}
template
unsigned num_bits_set(T x) {
return num_bits_set_count(reinterpret_cast(&x));
}Overall its fine. But you can get the same functionality from using
std::bitset and its more readable.int a {86};
std::bitset x(a);
// test bit 5
x[5]
// set bit 5
x[5] |= 1;
// reset bit 5
x[5] ^= 1;Code Snippets
template <typename T>
constexpr unsigned num_bits(T = 0) {
return sizeof(T) << 3;
}template <typename T>
unsigned num_bits_set(T x) {
unsigned cnt = 0;
// behavior of signed type underflow is unspecified
std::make_unsigned_t<T> v = x;
while (v) {
++cnt;
v &= v - 1;
}
return cnt;
}unsigned num_bits_set_count_value[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 000-015
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 016-031
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 032-047
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 048-063
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 064-079
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 080-095
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 096-111
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 112-127
// ---
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 128-143
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 144-159
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 160-175
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 176-191
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // 192-207
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 208-223
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // 224-239
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; // 240-255
// Use compile time meta program
// to unwind a multi byte value into a set of adds
// these will all be inlined.
template <int x>
unsigned num_bits_set_count(char const* byteStream) {
return num_bits_set_count_value[*byteStream]
+ num_bits_set_count<x - 1>(byteStream + 1);
}
template<> unsigned num_bits_set_count<0>(char const*) {return 0;}
template <typename T>
unsigned num_bits_set(T x) {
return num_bits_set_count<sizeof(T)>(reinterpret_cast<char const*>(&x));
}int a {86};
std::bitset<sizeof(a) * CHAR_BIT> x(a);
// test bit 5
x[5]
// set bit 5
x[5] |= 1;
// reset bit 5
x[5] ^= 1;Context
StackExchange Code Review Q#118424, answer score: 3
Revisions (0)
No revisions yet.