C#のビットフィールド
-
05-07-2019 - |
質問
だから、ビットフィールド。具体的には、大きなビットフィールド。私はビットフィールドの個々の値を操作する方法を理解していますが、次のような大きなセットでこれを行うにはどうすればよいですか:
uint[] bitfield = new uint[4] { 0x0080000, 0x00FA3020, 0x00C8000, 0x0FF00D0 };
私が抱えている特定の問題は、アレイ全体に渡って左右にシフトすることです。たとえば、>>を実行した場合、 4
上記の配列では、次のようになります:
uint[4] { 0x0008000, 0x000FA302, 0x000C800, 0x00FF00D };
今、ここでの(過度に)単純化されたアルゴリズムは次のように見えるかもしれません(これは私がその場でコードを書くことです):
int shift = 4;
for (int i = 0; i <= shift; i++) {
for (int j = bitfield.GetUpperBound(0); j > 0; j--) {
bitfield[j] = bitfield[j] >> 1;
bitfield[j] = bitfield[j] + ((bitfield[j-1] & 1) << (sizeof(uint)*8));
}
bitfield[0] = bitfield[0] >> 1;
}
この種のデータを簡単に操作できるものが組み込まれていますか?
解決
BitArrayが内部的にboolを使用していると思うのはなぜですかAPIの観点からビットを表すためにブール値を使用しますが、内部ではint []を使用すると考えています。
他のヒント
それが最善の方法かどうかはわかりませんが、これは機能します(シフトを0〜31の範囲に制限します。
public static void ShiftLeft(uint[] bitfield, int shift) {
if(shift < 0 || shift > 31) {
// handle error here
return;
}
int len = bitfield.Length;
int i = len - 1;
uint prev = 0;
while(i >= 0) {
uint tmp = bitfield[i];
bitfield[i] = bitfield[i] << shift;
if(i < len - 1) {
bitfield[i] |= (uint)(prev & (1 >> shift) - 1 ) >> (32 - shift);
}
prev = tmp;
i--;
}
}
public static void ShiftRight(uint[] bitfield, int shift) {
if(shift < 0 || shift > 31) {
// handle error here
return;
}
int len = bitfield.Length;
int i = 0;
uint prev = 0;
while(i < len) {
uint tmp = bitfield[i];
bitfield[i] = bitfield[i] >> shift;
if(i > 0) {
bitfield[i] |= (uint)(prev & (1 << shift) - 1 ) << (32 - shift);
}
prev = tmp;
i++;
}
}
PD:この変更により、31ビットを超えるシフトを処理できるようになります。リファクタリングして少しlessく見えるようにすることもできますが、私のテストでは機能しますが、パフォーマンス面ではそれほど悪くはないようです(実際に大きなビットセットを処理するために組み込まれているものがない限り)。
public static void ShiftLeft(uint[] bitfield, int shift) {
if(shift < 0) {
// error
return;
}
int intsShift = shift >> 5;
if(intsShift > 0) {
if(intsShift > bitfield.Length) {
// error
return;
}
for(int j=0;j < bitfield.Length;j++) {
if(j > intsShift + 1) {
bitfield[j] = 0;
} else {
bitfield[j] = bitfield[j+intsShift];
}
}
BitSetUtils.ShiftLeft(bitfield,shift - intsShift * 32);
return;
}
int len = bitfield.Length;
int i = len - 1;
uint prev = 0;
while(i >= 0) {
uint tmp = bitfield[i];
bitfield[i] = bitfield[i] << shift;
if(i < len - 1) {
bitfield[i] |= (uint)(prev & (1 >> shift) - 1 ) >> (32 - shift);
}
prev = tmp;
i--;
}
}
public static void ShiftRight(uint[] bitfield, int shift) {
if(shift < 0) {
// error
return;
}
int intsShift = shift >> 5;
if(intsShift > 0) {
if(intsShift > bitfield.Length) {
// error
return;
}
for(int j=bitfield.Length-1;j >= 0;j--) {
if(j >= intsShift) {
bitfield[j] = bitfield[j-intsShift];
} else {
bitfield[j] = 0;
}
}
BitSetUtils.ShiftRight(bitfield,shift - intsShift * 32);
return;
}
int len = bitfield.Length;
int i = 0;
uint prev = 0;
while(i < len) {
uint tmp = bitfield[i];
bitfield[i] = bitfield[i] >> shift;
if(i > 0) {
bitfield[i] |= (uint)(prev & (1 << shift) - 1 ) << (32 - shift);
}
prev = tmp;
i++;
}
}
拡張メソッドを使用して、これを行うことができます:
public static class BitArrayExtensions
{
public static void DownShift(this BitArray bitArray, int places)
{
for (var i = 0; i < bitArray.Length; i++)
{
bitArray[i] = i + places < bitArray.Length && bitArray[i + places];
}
}
public static void UpShift(this BitArray bitArray, int places)
{
for (var i = bitArray.Length - 1; i >= 0; i--)
{
bitArray[i] = i - places >= 0 && bitArray[i - places];
}
}
}
残念ながら、シフト演算子をオーバーロードする方法を思い付くことができませんでした。 (主に BitArray
が封印されているためです。)
int
sまたは uint
sを操作する場合は、 BitArray
にビットを挿入/抽出するための拡張メソッドを作成できます。 。 ( BitArray
には、 int
の配列を受け取るコンストラクターがありますが、それだけで終わります。)
これは、特にシフトについては説明していませんが、大きなセットで作業する場合には便利です。 Cにありますが、C#に簡単に適応できると思います