読者です 読者をやめる 読者になる 読者になる

任意のビットを任意のビット幅で取得する。

c++ tips

ビットマスクとシフトを組み合わせて実現してみたのでメモ。

組み込みで何かする場合や、PCでもハードウェアからのデータなどビットでパッキングされたデータを扱う場合に欲しくなる。

マクロの例

普段はマクロで実現したりする。

#define GET_4BITS(v, s) ((v&(0xf<<s))>>s)

任意の幅を取り出すのは難しい上に、何をやっているのかわかりづらい。

ビットマスクの計算

以下の要領で考えてみた。

  1. 指定幅のstd::bitsetを生成しすべて1に設定。
  2. 数値変換できる最大幅のto_uulong()に変換
  3. 指定ビット数左シフト

シフトと変換は逆になっても結果は変化しないと思うが。。。

template <int shift, int width, class Type>
inline Type getBitMask(void)
{
    return static_cast<Type>(std::bitset<width>().set().to_ullong() << shift);
}

std::bitsetでall 1の設定

すべてのbitを1にする方法を考えこんでしまった。

std::bitset<64>(~0)

このやり方だとOne's complement machineで互換性に問題があるらしいけどよく分からない。

The reason for that is that ~0 has to invert all bits. Inverting that will yield -1 on a two's complement machine (which is the value we need!), but will not yield -1 on another representation. On a one's complement machine, it yields zero. Thus, on a one's complement machine, the above will initialize a to zero.

ようするに~0だとすべてのビットが0にセットされるケースがあるので安全ではないと言うことらしい。

もっというと、これとかここにあるように、set()メソッドでよい。

std::bitset<64>().set()

遠回りしてしまった。

指定の値の特定のビットから特定のビット数取得

以下のような感じで考えた。

  1. マスクを作成
  2. マスクと値でANDを取得
  3. 指定ビット数右シフト
template <int shift, int width, class Type>
inline Type getBits(Type val)
{
    return ((val & getBitMask<shift, width, Type>()) >> shift);
}

整理してGistに置いた。