11111111のバイナリ値を持つバイトbがあると仮定します
たとえば、2番目のビットから始まる3ビットの整数値を読み取る、または5番目のビットから始まる4ビットの整数値を書き込むにはどうすればよいですか?
11111111のバイナリ値を持つバイトbがあると仮定します
たとえば、2番目のビットから始まる3ビットの整数値を読み取る、または5番目のビットから始まる4ビットの整数値を書き込むにはどうすればよいですか?
私がこの質問をしてから約2年以上経った今でも、私がまだ完全な初心者であり、プロセスを理解したい人々にとって最も有益であるときに、私がそれを説明したい方法で説明したいと思います。
まず、「11111111」の例の値を忘れてください。これは、プロセスの視覚的な説明に実際には適していません。したがって、初期値を10111011
(10進数で187)とします。これは、プロセスをもう少し説明します。
1-2番目のビットから始まる3ビット値の読み取り方法:
___ <- those 3 bits
10111011
値は101、つまり10進数で5です。これを取得するには、次の2つの方法があります。
このアプローチでは、必要なビットは最初に値00001110
(10進数の14)でマスクされ、その後、所定の位置にシフトされます。
___
10111011 AND
00001110 =
00001010 >> 1 =
___
00000101
この式は次のようになります。(value & 14) >> 1
このアプローチは似ていますが、演算の順序が逆になります。つまり、元の値がシフトされて00000111
から(7)でマスクされ、最後の3ビットだけが残ります。
___
10111011 >> 1
___
01011101 AND
00000111
00000101
この式は次のようになります。(value >> 1) & 7
どちらのアプローチも同じ量の複雑さを伴うため、パフォーマンスに違いはありません。
2- 2番目のビットから始まる3ビット値の書き込み方法:
この場合、初期値は既知であり、これがコードの場合である場合、既知の値をより少ない操作を使用する別の既知の値に設定する方法を思い付くことができるかもしれませんが、実際にはこれはめったにありませんほとんどの場合、コードは初期値も書き込まれる値も認識しません。
これは、新しい値がバイトに正常に「スプライス」されるためには、ターゲットビットをゼロに設定する必要があることを意味します。その後、シフトされた値が所定の位置に「スプライス」されます。これが最初のステップです。
___
10111011 AND
11110001 (241) =
10110001 (masked original value)
2番目のステップは、書き込みたい値を3ビットにシフトすることです。たとえば、値を101(5)から110(6)に変更します。
___
00000110 << 1 =
___
00001100 (shifted "splice" value)
3番目の最後のステップは、マスクされた元の値をシフトされた「スプライス」値でスプライスすることです。
10110001 OR
00001100 =
___
10111101
プロセス全体の式は次のようになります。(value & 241) | (6 << 1)
ボーナス-読み取りおよび書き込みマスクを生成する方法:
当然のことながら、特に32ビットおよび64ビットのコンテナーの場合、2進化10進コンバーターを使用することは、決して洗練されたものではありません。10進値は非常に大きくなります。式を使用してマスクを簡単に生成できます。これは、コンパイラがコンパイル中に効率的に解決できます。
((1 << fieldLength) - 1) << (fieldIndex - 1)
、最初のビットのインデックスが1(ゼロではない)であると想定(1 << fieldLength) - 1
インデックスは常に最初のビットにシフトされるため、ここではインデックスは役割を果たしません。~
演算子で反転するだけですそれはどのように機能しますか(上記の例の2番目のビットで始まる3ビットフィールドで)?
00000001 << 3
00001000 - 1
00000111 << 1
00001110 ~ (read mask)
11110001 (write mask)
同じ例が、より広い整数と任意のビット幅およびフィールドの位置に適用され、それに応じてシフト値とマスク値が変化します。
また、例では符号なし整数を想定しています。これは、整数を移植可能なビットフィールドの代替として使用するために使用するものです(通常のビットフィールドは、標準によって移植可能であることが保証されていません)、左シフトと右シフトの両方パディング0を挿入します。これは、符号付き整数を右シフトする場合には当てはまりません。
さらに簡単:
このマクロのセットを使用する(ただし、メンバー関数の生成に依存しているため、C ++でのみ):
#define GETMASK(index, size) ((((size_t)1 << (size)) - 1) << (index))
#define READFROM(data, index, size) (((data) & GETMASK((index), (size))) >> (index))
#define WRITETO(data, index, size, value) ((data) = (((data) & (~GETMASK((index), (size)))) | (((value) << (index)) & (GETMASK((index), (size))))))
#define FIELD(data, name, index, size) \
inline decltype(data) name() const { return READFROM(data, index, size); } \
inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }
あなたは次のような単純なものを選ぶことができます:
struct A {
uint bitData;
FIELD(bitData, one, 0, 1)
FIELD(bitData, two, 1, 2)
};
また、簡単にアクセスできるプロパティとしてビットフィールドを実装します。
A a;
a.set_two(3);
cout << a.two();
decltype
gccのtypeof
C++11より前のバージョンに置き換えます。
値をシフトしてマスクする必要があるため、たとえば...
最初の2ビットを読み取りたい場合は、次のようにマスクする必要があります。
int value = input & 0x3;
オフセットする場合は、右にNビットシフトしてから、必要なビットをマスクする必要があります。
int value = (intput >> 1) & 0x3;
あなたがあなたの質問で尋ねたように3ビットを読むこと。
int value = (input >> 1) & 0x7;
これを使用して、気軽に:
#define BitVal(data,y) ( (data>>y) & 1) /** Return Data.Y value **/
#define SetBit(data,y) data |= (1 << y) /** Set Data.Y to 1 **/
#define ClearBit(data,y) data &= ~(1 << y) /** Clear Data.Y to 0 **/
#define TogleBit(data,y) (data ^=BitVal(y)) /** Togle Data.Y value **/
#define Togle(data) (data =~data ) /** Togle Data value **/
例えば:
uint8_t number = 0x05; //0b00000101
uint8_t bit_2 = BitVal(number,2); // bit_2 = 1
uint8_t bit_1 = BitVal(number,1); // bit_1 = 0
SetBit(number,1); // number = 0x07 => 0b00000111
ClearBit(number,2); // number =0x03 => 0b0000011
シフトアンドマスク(AND)操作を行う必要があります。bを任意のバイト、pをnビット(> = 1)を取得するビットのインデックス(> = 0)とします。
まず、右にbをp回シフトする必要があります。
x = b >> p;
次に、結果をn個でマスクする必要があります。
mask = (1 << n) - 1;
y = x & mask;
すべてをマクロに入れることができます。
#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)
「たとえば、2番目のビットから始まる3ビットの整数値を読み取るにはどうすればよいですか?」
int number = // whatever;
uint8_t val; // uint8_t is the smallest data type capable of holding 3 bits
val = (number & (1 << 2 | 1 << 3 | 1 << 4)) >> 2;
(「2番目のビット」はビット#2、つまり実際には3番目のビットであると想定しました。)
バイトを読み取るには、std::bitsetを使用します
const int bits_in_byte = 8;
char myChar = 's';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
書き込むには、&^|などのビット演算子を使用する必要があります。&<<>>。彼らが何をしているのかを必ず学んでください。
たとえば、00100100を使用するには、最初のビットを1に設定し、<<>>演算子を使用して5回シフトする必要があります。書き込みを続行する場合は、最初のビットを設定してシフトし続けます。それは古いタイプライターに非常によく似ています:あなたは紙を書き、シフトします。
00100100の場合:最初のビットを1に設定し、5回シフトし、最初のビットを1に設定し、2回シフトします。
const int bits_in_byte = 8;
char myChar = 0;
myChar = myChar | (0x1 << 5 | 0x1 << 2);
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
int x = 0xFF; //your number - 11111111
たとえば、2番目のビットから始まる3ビットの整数値を読み取るにはどうすればよいですか?
int y = x & ( 0x7 << 2 ) // 0x7 is 111
// and you shift it 2 to the left
データからビットを取得し続ける場合は、ビットフィールドを使用することをお勧めします。構造体を設定し、1と0のみをロードする必要があります。
struct bitfield{
unsigned int bit : 1
}
struct bitfield *bitstream;
その後、このようにロードします(charをintまたはロードしているデータに置き換えます):
long int i;
int j, k;
unsigned char c, d;
bitstream=malloc(sizeof(struct bitfield)*charstreamlength*sizeof(char));
for (i=0; i<charstreamlength; i++){
c=charstream[i];
for(j=0; j < sizeof(char)*8; j++){
d=c;
d=d>>(sizeof(char)*8-j-1);
d=d<<(sizeof(char)*8-1);
k=d;
if(k==0){
bitstream[sizeof(char)*8*i + j].bit=0;
}else{
bitstream[sizeof(char)*8*i + j].bit=1;
}
}
}
次に、要素にアクセスします。
bitstream[bitpointer].bit=...
また
...=bitstream[bitpointer].bit
アームはビッグエンディアンまたはリトルエンディアンである可能性があるため、これはすべて、アームではなくi86/64で動作していることを前提としています。