C++ std::has_single_bit for Power of Two Checks
This article explores std::has_single_bit, a utility
function introduced in C++20 via the <bit> header
that determines whether an unsigned integer is an exact power of two. It
covers the underlying mechanics of the function, its syntax and
constraints, how it handles edge cases like zero, and why it provides a
safer, more readable, and often faster alternative to traditional
bitwise hacks.
What is
std::has_single_bit?
Introduced in C++20, std::has_single_bit is a standard
library function defined in the <bit> header. Its
primary function is to check whether a given unsigned integer value has
exactly one bit set to 1 in its binary representation.
In binary mathematics, every non-zero power of two (\(2^0 = 1, 2^1 = 2, 2^2 = 4, 2^3 = 8, \dots\)) contains exactly one set bit:
- \(1_{10} = 00000001_2\)
- \(2_{10} = 00000010_2\)
- \(4_{10} = 00000100_2\)
- \(8_{10} = 00001000_2\)
Because of this property, testing if an unsigned integer has a single set bit is equivalent to testing if it is a power of two.
Function Signature and Constraints
The function is defined as:
template< class T >
constexpr bool has_single_bit( T x ) noexcept;Key characteristics include:
- Type Constraints: The template argument
Tmust be an unsigned integer type (such asunsigned int,uint8_t,uint32_t,uint64_t, orsize_t). Passing a signed integer (likeintorint64_t) results in a compile-time error. - Compile-Time Evaluation: It is declared
constexpr, allowing it to be evaluated at compile time for static assertions and template metaprogramming. - No Exceptions: It is marked
noexcept, guaranteeing that it will not throw runtime exceptions.
Handling Edge Cases
A frequent challenge in power-of-two algorithms is properly handling
the value 0. While 0 is not a power of two,
poorly written bitwise formulas often require a separate check to avoid
misidentifying it.
std::has_single_bit handles zero natively:
- If
x == 0, there are no set bits, sostd::has_single_bit(0u)correctly returnsfalse.
Advantages Over Traditional Bitwise Idioms
Prior to C++20, the standard idiom for checking if a number was a power of two was:
bool is_power_of_two = (x > 0) && ((x & (x - 1)) == 0);While effective, std::has_single_bit offers distinct
advantages:
- Readability: The function name explicitly conveys
the intent, reducing cognitive load and the risk of operator precedence
bugs (such as omitting parentheses around
x & (x - 1)). - Type Safety: The standard concept enforcement ensures the function is never inadvertently used with signed types, which can produce undefined behavior during bit shifts or arithmetic underflow.
- Compiler Optimization: Compilers often map
std::has_single_bitdirectly to hardware-level intrinsics or instructions, such as population count (POPCNT) instructions, generating optimal machine code for the target architecture.
Code Example
#include <iostream>
#include <bit>
#include <cstdint>
int main() {
constexpr uint32_t val1 = 16;
constexpr uint32_t val2 = 18;
constexpr uint32_t val3 = 0;
// Evaluated at compile-time
static_assert(std::has_single_bit(val1) == true);
static_assert(std::has_single_bit(val2) == false);
static_assert(std::has_single_bit(val3) == false);
std::cout << val1 << " is power of two: " << std::has_single_bit(val1) << '\n';
std::cout << val2 << " is power of two: " << std::has_single_bit(val2) << '\n';
std::cout << val3 << " is power of two: " << std::has_single_bit(val3) << '\n';
return 0;
}