shimada-kの日記

ソフトウェア・エンジニアのブログです

ビット演算用のライブラリを公開します

私が以前から使用していたビット演算のC言語用ライブラリを公開します。

  • 低レイヤ技術を扱う
  • マルチメディアを扱う
  • オブジェクトの領域を節約したい

などの場合、ビット演算を行うことがあると思います。ただ、C言語で用意されているものは演算子だけで、「この変数のこのビットをごにょごにょする」といった場合は自前で関数を作成する必要があります。

私は研究(趣味)でレジスタの値をごにょごにょしたりするので、その時にいちいち論理演算子を使って関数を作るのはオーバーヘッドが大きいわけです。なので、32bit変数用のライブラリを作成し、使用していました。bitopsというライブラリです。

しかし、最近64bit変数のビット演算を行う必要が出て、従来のライブラリに64bit変数用の関数を追加したので、ついでに32bit関数達もリファクタリングして、テスト用のプログラムも作ってみました*1

ブログのエントリにライブラリ本体を載せると非常に縦長なブログになるので、ここでは載せません。
#ライブラリ本体はgithubにあげてあります。

ヘッダファイルです。ライブラリ使用時にincludeします。
bitops.h

/* 桁数をチェックし不正ならコンソールに表示するマクロ */
#define invalid_order(n, order, __func__)	if(n > order - 1){			\
							printf(__func__);		\
							puts(":out of order");	\
						}

typedef unsigned long u32;

u32 pick_nbit32(u32 num, u32 n);
void set_nbit32(u32 *num, u32 n);
void clr_nbit32(u32 *num, u32 n);
void rotate_nbit32(u32 *num, u32 n);
u32 find_first_setbit32(u32 num);
u32 find_next_setbit32(u32 num, u32 n);
void print_binary32(u32 num);

typedef unsigned long long u64;

u32 pick_nbit64(u64 num, u32 n);
void set_nbit64(u64 *num, u32 n);
void clr_nbit64(u64 *num, u32 n);
void rotate_nbit64(u64 *num, u32 n);
u32 find_first_setbit64(u64 num);
u32 find_next_setbit64(u64 num, u32 n);
void print_binary64(u64 num);

ライブラリ側では基本的なビット演算用の関数は揃えてあるかな、と思います。機能だけ書き出すと

  • あるビットが0か1か調べる関数(pick_nbit系)
  • あるビットを1にする関数 (set_nbit系)
  • あるビットを0にする関数(clr_nbit系)
  • あるビットを反転する関数(rotate_nbit系)
  • 変数の中で1である最初のビットの位置を返す関数(find_first_setbit系)
  • 変数の中であるビットの次に1であるビットの位置を返す関数(find_next_setbit系)
  • 2進数でコンソールに表示する関数(print_binary系)

の7種類です。これが32bitと64bitで2種類、合計14の関数があります。

次にbitops.cをテストするプログラムです
test.c

#include <stdio.h>
#include <stdlib.h>	/* atoi(3) */
#include "bitops.h"

/*
	test.c:bitops.cのテストプログラム
	written by shimada-k
	last modify 2011.6.17
*/

void rotate_test32(void)
{
	int i;
	u32 hoge = 0;

	puts("rotate_test");

	print_binary32(hoge);	/* 表示テスト */

	putchar('\n');

	for(i = 0; i < 32; i += 2){
		set_nbit32(&hoge, i);	/* 0から1個飛ばしにビットをセット */
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');

	for(i = 1; i < 32; i += 2){
		set_nbit32(&hoge, i);	/* 1から1個飛ばしにビットをセット */
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');

	for(i = 0; i < 32; i += 2){		/* 0から1個飛ばしにビットを反転 */
		rotate_nbit32(&hoge, i);
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');

	for(i = 1; i < 32; i += 2){		/* 1から1個飛ばしにビットを反転 */
		rotate_nbit32(&hoge, i);
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');
}

void rotate_test64(void)
{
	int i;
	u64 hoge = 0;

	puts("rotate_test");

	print_binary64(hoge);	/* 表示テスト */

	putchar('\n');

	for(i = 0; i < 64; i += 2){
		set_nbit64(&hoge, i);	/* 0から1個飛ばしにビットをセット */
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');

	for(i = 1; i < 64; i += 2){
		set_nbit64(&hoge, i);	/* 1から1個飛ばしにビットをセット */
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');

	for(i = 0; i < 64; i += 2){		/* 0から1個飛ばしにビットを反転 */
		rotate_nbit64(&hoge, i);
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');

	for(i = 1; i < 64; i += 2){		/* 1から1個飛ばしにビットを反転 */
		rotate_nbit64(&hoge, i);
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');
}

void bitpos_test32(void)
{
	int i;
	u32 hoge = 0;

	puts("bitpos_test");

	for(i = 0; i < 32; i += 2){		/* 0から1個飛ばしにビットをセット */
		set_nbit32(&hoge, i);
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');

	for(i = 0; i < 32; i = find_next_setbit32(hoge, i)){
		clr_nbit32(&hoge, i);
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');

	for(i = 1; i < 32; i *= 2){		/* 1からiの2倍の間隔を空けながらビットをセット */
		set_nbit32(&hoge, i);
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');

	for(i = 0; i < 32; i = find_next_setbit32(hoge, i)){
		clr_nbit32(&hoge, i);
	}

	print_binary32(hoge);	/* 表示 */
	putchar('\n');
}

void bitpos_test64(void)
{
	int i;
	u64 hoge = 0;

	puts("bitpos_test");

	for(i = 0; i < 64; i += 2){		/* 0から1個飛ばしにビットをセット */
		set_nbit64(&hoge, i);
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');

	for(i = 0; i < 64; i = find_next_setbit64(hoge, i)){
		clr_nbit64(&hoge, i);
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');

	for(i = 1; i < 64; i *= 2){		/* 1からiの2倍の間隔を空けながらビットをセット */
		set_nbit64(&hoge, i);
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');

	for(i = 0; i < 64; i = find_next_setbit64(hoge, i)){
		clr_nbit64(&hoge, i);
	}

	print_binary64(hoge);	/* 表示 */
	putchar('\n');
}

int main(int argc, char *argv[])
{
	int bit_width = 32;	/* デフォルトは32ビットテスト */

	if(argc == 2){	/* 引数があるなら受ける取る */
		bit_width = atoi(argv[1]);
	}

	if(bit_width == 32){
		puts("----32 bit operation----");
		rotate_test32();
		bitpos_test32();
	}
	else if(bit_width == 64){
		puts("----64 bit operation----");
		rotate_test64();
		bitpos_test64();
	}

	return 0;
}

引数でテストする桁数を指定します。32か64です。指定なしの場合は32bit用のAPI群がテストされます。テストのやり方としては、「変数のビットを立てる->ビットをもとに戻す->もとの変数と値が一致するか?」ということをやっています。

テストプログラムの実行の結果です。

./test 32
f:id:shimada-k:20110617185144p:image:w360

./test 64
f:id:shimada-k:20110617185145p:image:w360

いいですね。予期した動作をしています。

※今後の課題
8bit変数用の関数も作成する
マクロ化できるところはマクロ化する
エラー処理を実装して異常系のテストを実施する

*1:既にこのようなライブラリはあるのかもしれませんが、私が探した限り無かったのと、シンプル&軽量でかつ使いやすいものを作りたかったので自前で用意しました。最悪、車輪の再開発でも少なくとも車輪を作る技術は身に付きます。