Subscribed unsubscribe Subscribe Subscribe

2進数リテラルが欲しいときのまとめ

たとえば組み込みで,2進数っぽい文法で数値を扱いたいときがある.ドットマトリクスに顔文字アイコンを表示させるときに

set_icon_data(
    0x0a, //  1 1 
    0x0a, //  1 1 
    0x00, //      
    0x11, // 1   1
    0x0e  //  111
);

みたいにベタ書きなのはあまりにも悲しい.顔文字をコードで直接記述したい!でもC/C++は2進表記を直接サポートしない.じゃあどうするか.

コンパイラの拡張構文を使う

コンパイラによっては"0b00100000"みたいなリテラルで2進数を扱える場合もある.4.3以降のgccでもそう.

set_icon_data(
    0b01010,
    0b01010,
    0b00000,
    0b10001,
    0b01110
);

マクロでbinary literal風事前定義

2進数表記のようなマクロを事前に定義しておく.数が多いなら機械的に生成してあげればOK.

#define b00001 1
#define b00010 2
...

set_icon_data(
    b01010,
    b01010,
    b00000,
    b10001,
    b01110
);

1ビットシフトへの置換

次の手はプリプロセッサに頑張ってもらう方式.定数式なのでコンパイル時に全部計算され,機械語の時点では上と全く一緒になっている.

#define $ (((((0
#define X  <<1|1)
#define _  <<1)

set_icon_data(
    $ _ _ _ _ _,
    $ _ X _ X _,
    $ _ X _ X _,
    $ _ _ _ _ _,
    $ X _ _ _ X,
    $ _ X X X _
);

アイコンっぽくて楽しい感じ.

8進数を介して2進リテラルもどきを生成

8進数で"1011"は2進数で"001 000 001 001"になる.文字列結合演算子##を使って8進数表現にしてから3nビット目をnビット目にマッピングすることで,"1011"から11を得ることができる.これも得られるバイナリは一緒.

#define PP_HEX2BIN(b) ( \
 ((b & 1 <<  0) >>  0) + ((b & 1 <<  3) >>  2) + ((b & 1 <<  6) >>  4) + \
 ((b & 1 <<  9) >>  6) + ((b & 1 << 12) >>  8) + ((b & 1 << 15) >> 10) + \
 ((b & 1 << 18) >> 12) + ((b & 1 << 21) >> 14))

#define B(x) PP_HEX2BIN(0 ## x)

set_icon_data(
    B(01010),
    B(01010),
    B(00000),
    B(10001),
    B(01110)
);

(C++)boostを使う

上と似た機能はboostでも提供されているので,C++なら実装しなくてもよい.こっちは間にスペースが入っていてもOK.

#include <boost/utility/binary.hpp>

#define B(x) BOOST_BINARY(x)

set_icon_data(
    B(01010),
    B(01010),
    B(00000),
    B(10001),
    B(01110)
);

// これでもOK
#define _ 0

set_icon_data(
    B(_ 1 _ 1 _),
    B(_ 1 _ 1 _),
    B(_ _ _ _ _),
    B(1 _ _ _ 1),
    B(_ 1 1 1 _)
);

(C++0x)constexpr

constexprを使ってコンパイル時に評価.演算子を使えると絵としての表現力が上がりますね!
参考:二進数リテラル(嘘) - とくにあぶなくないRiSKのブログ

set_icon_data(
    -+-+-b(),
    -+-+-b(),
    -----b(),
    +---+b(),
    -+++-b()
);