<bit> Support

Description

The library provides thin wrappers around the C++20 <bit> functions for safe integer types. Each function extracts the underlying value, delegates to the corresponding std:: function, and wraps the result back into the safe type where appropriate. For u128, the functions delegate to the boost::int128 implementations.

#include <boost/safe_numbers/bit.hpp>

Power-of-Two Functions

has_single_bit

template <unsigned_library_type UnsignedInt>
constexpr auto has_single_bit(UnsignedInt x) noexcept -> bool;

Returns true if x is a power of two. See std::has_single_bit.

bit_ceil

template <unsigned_library_type UnsignedInt>
constexpr auto bit_ceil(UnsignedInt x) noexcept -> UnsignedInt;

Returns the smallest power of two not less than x. Undefined behavior if the result is not representable in the underlying type. See std::bit_ceil.

bit_floor

template <unsigned_library_type UnsignedInt>
constexpr auto bit_floor(UnsignedInt x) noexcept -> UnsignedInt;

Returns the largest power of two not greater than x. Returns zero if x is zero. See std::bit_floor.

bit_width

template <unsigned_library_type UnsignedInt>
constexpr auto bit_width(UnsignedInt x) noexcept -> int;

Returns the number of bits needed to represent x (i.e., 1 + floor(log2(x)) for x > 0, or 0 for x == 0). See std::bit_width.

Rotation Functions

rotl

template <unsigned_library_type UnsignedInt>
constexpr auto rotl(UnsignedInt x, int s) noexcept -> UnsignedInt;

Computes the result of bitwise left-rotating x by s positions. See std::rotl.

rotr

template <unsigned_library_type UnsignedInt>
constexpr auto rotr(UnsignedInt x, int s) noexcept -> UnsignedInt;

Computes the result of bitwise right-rotating x by s positions. See std::rotr.

Counting Functions

countl_zero

template <unsigned_library_type UnsignedInt>
constexpr auto countl_zero(UnsignedInt x) noexcept -> int;

Returns the number of consecutive 0-bits starting from the most significant bit. See std::countl_zero.

countl_one

template <unsigned_library_type UnsignedInt>
constexpr auto countl_one(UnsignedInt x) noexcept -> int;

Returns the number of consecutive 1-bits starting from the most significant bit. See std::countl_one.

countr_zero

template <unsigned_library_type UnsignedInt>
constexpr auto countr_zero(UnsignedInt x) noexcept -> int;

Returns the number of consecutive 0-bits starting from the least significant bit. See std::countr_zero.

countr_one

template <unsigned_library_type UnsignedInt>
constexpr auto countr_one(UnsignedInt x) noexcept -> int;

Returns the number of consecutive 1-bits starting from the least significant bit. See std::countr_one.

popcount

template <unsigned_library_type UnsignedInt>
constexpr auto popcount(UnsignedInt x) noexcept -> int;

Returns the number of 1-bits in x. See std::popcount.

Byte Manipulation

byteswap

template <unsigned_library_type UnsignedInt>
constexpr auto byteswap(UnsignedInt x) noexcept -> UnsignedInt;

Reverses the bytes of x. For standard unsigned types this delegates to std::byteswap (C++23). For u128 this delegates to the boost::int128::byteswap implementation. See std::byteswap.

Examples

Example 1. This example demonstrates the bit manipulation functions.
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/bit.hpp>
#include <iostream>

int main()
{
    using namespace boost::safe_numbers;

    const u32 x {0b0000'0000'0000'0000'0000'0000'0010'1000};  // 40

    // Power-of-two queries
    std::cout << "has_single_bit(40) = " << has_single_bit(x) << '\n';
    std::cout << "has_single_bit(32) = " << has_single_bit(u32{32}) << '\n';
    std::cout << "bit_ceil(40)       = " << static_cast<std::uint32_t>(bit_ceil(x)) << '\n';
    std::cout << "bit_floor(40)      = " << static_cast<std::uint32_t>(bit_floor(x)) << '\n';
    std::cout << "bit_width(40)      = " << bit_width(x) << '\n';

    std::cout << '\n';

    // Rotation
    const u8 y {0b1011'0001};  // 177
    std::cout << "rotl(0b10110001, 2) = " << static_cast<unsigned>(rotl(y, 2)) << '\n';
    std::cout << "rotr(0b10110001, 2) = " << static_cast<unsigned>(rotr(y, 2)) << '\n';

    std::cout << '\n';

    // Counting
    const u16 z {0b0000'1111'0000'0000};  // 3840
    std::cout << "countl_zero(0x0F00) = " << countl_zero(z) << '\n';
    std::cout << "countl_one(0x0F00)  = " << countl_one(z) << '\n';
    std::cout << "countr_zero(0x0F00) = " << countr_zero(z) << '\n';
    std::cout << "countr_one(0x0F00)  = " << countr_one(z) << '\n';
    std::cout << "popcount(0x0F00)    = " << popcount(z) << '\n';

    std::cout << '\n';

    // Byteswap
    const u32 w {0x12345678};
    std::cout << std::hex;
    std::cout << "byteswap(0x12345678) = 0x" << static_cast<std::uint32_t>(byteswap(w)) << '\n';

    return 0;
}

Output:

has_single_bit(40) = 0
has_single_bit(32) = 1
bit_ceil(40)       = 64
bit_floor(40)      = 32
bit_width(40)      = 6

rotl(0b10110001, 2) = 198
rotr(0b10110001, 2) = 108

countl_zero(0x0F00) = 4
countl_one(0x0F00)  = 0
countr_zero(0x0F00) = 8
countr_one(0x0F00)  = 0
popcount(0x0F00)    = 4

byteswap(0x12345678) = 0x78563412