ビットマスクとシフトを組み合わせて実現してみたのでメモ。
組み込みで何かする場合や、PCでもハードウェアからのデータなどビットでパッキングされたデータを扱う場合に欲しくなる。
マクロの例
普段はマクロで実現したりする。
#define GET_4BITS(v, s) ((v&(0xf<<s))>>s)
任意の幅を取り出すのは難しい上に、何をやっているのかわかりづらい。
ビットマスクの計算
以下の要領で考えてみた。
- 指定幅のstd::bitsetを生成しすべて1に設定。
- 数値変換できる最大幅のto_uulong()に変換
- 指定ビット数左シフト
シフトと変換は逆になっても結果は変化しないと思うが。。。
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()
遠回りしてしまった。
指定の値の特定のビットから特定のビット数取得
以下のような感じで考えた。
- マスクを作成
- マスクと値でANDを取得
- 指定ビット数右シフト
template <int shift, int width, class Type> inline Type getBits(Type val) { return ((val & getBitMask<shift, width, Type>()) >> shift); }
整理してGistに置いた。