ぷろみん

プログラミング的な内容を扱ってます

中途半端なbit幅の符号付整数を扱い易くするSign extending trick

charじゃ大きすぎる

charは何気に256もの数字を記録できます。
しかし、組み込みのような制限の厳しい環境では1bitでも送受信のデータは節約したいものです。
そんな時はbit演算を駆使してデータをやりとりすることになるのですが、困った状況というものがあります。

例えば、あるセンサーが-10~+10の値を返す場合どうやって値を管理すれば良いのでしょうか?
負の値を表現するには通常2の補数表現が使われますが、それを自分でやるしかないのでしょうか?
実はbit fieldを使えばそんな問題も簡単に解決できるようになります。

bit field

知らない人も多そうですが、データのやり取りの際にUnion等と一緒に使われることが多い機能です。 bit fieldでは指定した変数に割り当てるbit数が指定できます。
極々ローレベルな問題を扱うことが多いCならではの機能といえるでしょう。
書き方としては単純で、クラスや構造体のメンバ変数の最後にコロンと割り当てるbit数を書くだけです。

struct BitField
{
    int a: 3;
};

int main()
{
    BitField bitField = {};
    bitField.a = 0b0000; // 0
    bitField.a = 0b0001; // 1
    bitField.a = 0b0010; // 2
    bitField.a = 0b0011; // 3
    bitField.a = 0b0100; // -4
    bitField.a = 0b0101; // -3
    bitField.a = 0b0110; // -2
    bitField.a = 0b0111; // -1
    bitField.a = 0b1000; // 0
    bitField.a = 0b1001; // 1
    bitField.a = 0b1010; // 2
    bitField.a = 0b1011; // 3
    bitField.a = 0b1100; // -4
    bitField.a = 0b1101; // -3
    bitField.a = 0b1110; // -2
    bitField.a = 0b1111; // -1

これでaには3bit以上の値が入らないようになります。
4bit目以降の数値は無視されていることが分かると思います。
そして、3bit目が補数表現のための負値の役割を果たしていることも分かります。

Sign extending trick

template <typename T, unsigned B>
inline T signextend(const T x)
{
  struct {T x:B;} s;
  return s.x = x;
}

int r = signextend<signed int,5>(x);  // 5bitの符号付整数をintに変換する

まとめ

これで1byteに符号付整数を2つ入れてくるようなセンサーを相手にしても頭を抱えなくて済みますね。
template引数をbit fieldにも使う事ができるって面白かったので紹介してみました。
頭の片隅に置いておくといつか役に立つ日がくるかもしれません。

参考

http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend