はじめに
ビット演算は、パフォーマンスが要求される場面や、低レベルのデータ操作で不可欠なテクニックです。従来は、ビットシフトや論理演算子を駆使して手動で実装する必要がありましたが、コードが複雑になり、環境依存の問題も発生しがちでした。
C++20で導入された**<bit>
**ヘッダーは、このようなビット操作を行うための、標準化された一連の関数を提供します。これにより、誰が書いても同じように動作する、安全で読みやすいビット演算が可能になります。
この記事では、<bit>
ライブラリで提供される主要なビット操作関数について、その使い方をサンプルコードと共に解説します。
【前提】C++20とは?
C++20は、2020年に正式化されたC++言語のメジャーアップデート版です。この記事で紹介する機能はC++20で導入されたため、利用するにはC++20に対応したコンパイラが必要です。
ビット演算関数のサンプルコード
完成コード
#include <iostream>
#include <bit> // ビット操作関数
#include <cstdint> // 型(uint8_tなど)
#include <iomanip> // 16進数表示(setw, setfill)
// C++20より前の環境では、using namespace std; は推奨されない場合がある
using std::cout;
using std::endl;
using std::hex;
using std::setw;
using std::setfill;
using namespace std;
int main() {
// --- 2のべき乗か判定: has_single_bit ---
cout << "--- 2のべき乗判定 ---" << endl;
cout << "8u (1000) は2のべき乗? -> " << boolalpha << has_single_bit(8u) << endl;
cout << "6u (0110) は2のべき乗? -> " << boolalpha << has_single_bit(6u) << endl;
// --- 2のべき乗への切り上げ/切り下げ: bit_ceil, bit_floor ---
cout << "\n--- 2のべき乗へのまるめ ---" << endl;
cout << "9u (1001) を切り上げ -> " << bit_ceil(9u) << endl; // -> 16 (10000)
cout << "9u (1001) を切り下げ -> " << bit_floor(9u) << endl; // -> 8 (1000)
// --- 値を表現するのに必要なビット幅: bit_width ---
cout << "\n--- 必要なビット幅 ---" << endl;
cout << "6u (110) を表現するのに必要な幅: " << bit_width(6u) << " ビット" << endl; // -> 3
// --- ビットの回転: rotl, rotr ---
cout << "\n--- ビット回転 ---" << endl;
uint8_t bits = 0b11001010;
cout << "元: " << hex << setw(2) << setfill('0') << (int)bits << endl;
cout << "2ビット左回転(rotl): " << hex << setw(2) << setfill('0') << (int)rotl(bits, 2) << endl; // -> 00101011
cout << "2ビット右回転(rotr): " << hex << setw(2) << setfill('0') << (int)rotr(bits, 2) << endl; // -> 10110010
// --- ビット数を数える: countl_zero, popcountなど ---
cout << "\n--- ビット数を数える ---" << endl;
uint8_t count_bits = 0b00111010;
cout << "対象: " << hex << setw(2) << setfill('0') << (int)count_bits << endl;
cout << "先頭から続く0の数 (countl_zero): " << countl_zero(count_bits) << endl; // -> 2
cout << "末尾から続く0の数 (countr_zero): " << countr_zero(count_bits) << endl; // -> 1
cout << "立っているビット(1)の総数 (popcount): " << popcount(count_bits) << endl; // -> 4
return 0;
}
各関数の解説
has_single_bit(x)
x
が2のべき乗であるか(つまり、2進数表現で1
のビットが一つだけ立っているか)をtrue
/false
で返します。
bit_ceil(x)
/ bit_floor(x)
bit_ceil(x)
:x
以上の最小の2のべき乗の値を返します(切り上げ)。bit_floor(x)
:x
以下の最大の2のべき乗の値を返します(切り下げ)。
bit_width(x)
x
の値を表現するために最低限必要なビット数を返します。0
の場合は0
を返します。
rotl(x, s)
/ rotr(x, s)
ビットを**回転(rotate)**させます。シフト演算とは異なり、はみ出したビットは反対側から入ってきます。
rotl(x, s)
:x
のビットをs
だけ左に回転させます。rotr(x, s)
:x
のビットをs
だけ右に回転させます。
ビット数を数える関数群
countl_zero(x)
:l
=left。左側(上位ビット)から連続している0
の数を数えます。countr_zero(x)
:r
=right。右側(下位ビット)から連続している0
の数を数えます。countl_one(x)
/countr_one(x)
: 同様に、連続している1
の数を数えます。popcount(x)
:population count
の略。x
に含まれる1
のビットの総数を数えます。
まとめ
今回は、C++20の<bit>
ライブラリが提供する、便利なビット操作関数をまとめて解説しました。これらの標準関数を使うことで、従来は複雑なビット演算の組み合わせで実現していた処理を、意図が明確で、移植性の高い、安全なコードで記述することができます。