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 T must be an unsigned integer type (such as unsigned int, uint8_t, uint32_t, uint64_t, or size_t). Passing a signed integer (like int or int64_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, so std::has_single_bit(0u) correctly returns false.

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:

  1. 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)).
  2. 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.
  3. Compiler Optimization: Compilers often map std::has_single_bit directly 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;
}