<bit> Support

Description

The library provides thin wrappers around the C++20 <bit> functions for all safe integer types, including bounded_uint<Min, Max>. 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 <non_bounded_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.

rotl is not available for bounded_uint types. Bit rotation can produce values outside the valid bounded range, making the operation semantically meaningless for bounded integers.

rotr

template <non_bounded_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.

rotr is not available for bounded_uint types. Bit rotation can produce values outside the valid bounded range, making the operation semantically meaningless for bounded integers.

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 <non_bounded_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.

byteswap is not available for bounded_uint types. Byte reversal can produce values outside the valid bounded range, making the operation semantically meaningless for bounded integers.

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/bounded_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';

    std::cout << std::dec << '\n';

    // Bounded integer types work the same way
    using byte_val = bounded_uint<0u, 255u>;
    using word_val = bounded_uint<0u, 65535u>;

    const auto bv = byte_val{0b0010'1000u};  // 40
    std::cout << "bounded has_single_bit(40) = " << has_single_bit(bv) << '\n';
    std::cout << "bounded bit_ceil(40)       = " << static_cast<unsigned>(static_cast<std::uint8_t>(bit_ceil(bv))) << '\n';
    std::cout << "bounded bit_floor(40)      = " << static_cast<unsigned>(static_cast<std::uint8_t>(bit_floor(bv))) << '\n';
    std::cout << "bounded bit_width(40)      = " << bit_width(bv) << '\n';
    std::cout << "bounded popcount(40)       = " << popcount(bv) << '\n';

    const auto wv = word_val{0x0F00u};
    std::cout << "bounded countl_zero(0x0F00) = " << countl_zero(wv) << '\n';
    std::cout << "bounded popcount(0x0F00)    = " << popcount(wv) << '\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

bounded has_single_bit(40) = 0
bounded bit_ceil(40)       = 64
bounded bit_floor(40)      = 32
bounded bit_width(40)      = 6
bounded popcount(40)       = 2
bounded countl_zero(0x0F00) = 4
bounded popcount(0x0F00)    = 4