整数のサイズとエンディアンを無視した整数のビット反転
-
09-06-2019 - |
質問
整数の typedef を指定すると、次のようになります。
typedef unsigned int TYPE;
または
typedef unsigned long TYPE;
整数のビットを反転する次のコードがあります。
TYPE max_bit= (TYPE)-1;
void reverse_int_setup()
{
TYPE bits= (TYPE)max_bit;
while (bits <<= 1)
max_bit= bits;
}
TYPE reverse_int(TYPE arg)
{
TYPE bit_setter= 1, bit_tester= max_bit, result= 0;
for (result= 0; bit_tester; bit_tester>>= 1, bit_setter<<= 1)
if (arg & bit_tester)
result|= bit_setter;
return result;
}
最初に reverse_int_setup() を実行するだけで済みます。これは、最上位ビットがオンになった整数を格納し、次に reverse_int() を呼び出します。引数) 戻り値 引数 ビットが反転されています (増加するカウンターから取得されたバイナリ ツリーへのキーとして使用されますが、それは多かれ少なかれ無関係です)。
reverse_int_setup() の呼び出し後、コンパイル時に max_int の正しい値を取得するプラットフォームに依存しない方法はありますか?そうでない場合、検討しているアルゴリズムはありますか より良い/よりスリムな 私が持っている reverse_int() のものよりも?
ありがとう。
解決
#include<stdio.h>
#include<limits.h>
#define TYPE_BITS sizeof(TYPE)*CHAR_BIT
typedef unsigned long TYPE;
TYPE reverser(TYPE n)
{
TYPE nrev = 0, i, bit1, bit2;
int count;
for(i = 0; i < TYPE_BITS; i += 2)
{
/*In each iteration, we swap one bit on the 'right half'
of the number with another on the left half*/
count = TYPE_BITS - i - 1; /*this is used to find how many positions
to the left (and right) we gotta move
the bits in this iteration*/
bit1 = n & (1<<(i/2)); /*Extract 'right half' bit*/
bit1 <<= count; /*Shift it to where it belongs*/
bit2 = n & 1<<((i/2) + count); /*Find the 'left half' bit*/
bit2 >>= count; /*Place that bit in bit1's original position*/
nrev |= bit1; /*Now add the bits to the reversal result*/
nrev |= bit2;
}
return nrev;
}
int main()
{
TYPE n = 6;
printf("%lu", reverser(n));
return 0;
}
今回は TK の「ビット数」のアイデアを使用しましたが、1 バイトに 8 ビットが含まれると仮定せず、代わりに CHAR_BIT マクロを使用することで、移植性をいくらか高めました。コードはより効率的になりました (内部の for ループが削除されました)。今回はコードの難解さがもう少し軽減されることを願っています。:)
count を使用する必要があるのは、ビットをシフトする必要がある位置の数が反復ごとに異なるためです。右端のビットを 31 位置 (32 ビット数と仮定) 移動し、右から 2 番目のビットを 29 位置移動する必要があります。の上。したがって、i が増加するにつれて、反復ごとに count が減少する必要があります。
この情報がコードを理解するのに役立つことを願っています...
他のヒント
次のプログラムは、ビットを反転するための効率的なアルゴリズムを示しています。このアルゴリズムは、64 ビット数値を処理するために簡単に拡張できます。
#include <stdio.h>
#include <stdint.h>
int main(int argc, char**argv)
{
int32_t x;
if ( argc != 2 )
{
printf("Usage: %s hexadecimal\n", argv[0]);
return 1;
}
sscanf(argv[1],"%x", &x);
/* swap every neigbouring bit */
x = (x&0xAAAAAAAA)>>1 | (x&0x55555555)<<1;
/* swap every 2 neighbouring bits */
x = (x&0xCCCCCCCC)>>2 | (x&0x33333333)<<2;
/* swap every 4 neighbouring bits */
x = (x&0xF0F0F0F0)>>4 | (x&0x0F0F0F0F)<<4;
/* swap every 8 neighbouring bits */
x = (x&0xFF00FF00)>>8 | (x&0x00FF00FF)<<8;
/* and so forth, for say, 32 bit int */
x = (x&0xFFFF0000)>>16 | (x&0x0000FFFF)<<16;
printf("0x%x\n",x);
return 0;
}
このコードにはエラーが含まれていてはならず、0x12345678 を使用してテストされ、正しい答えである 0x1e6a2c48 が生成されました。
typedef unsigned long TYPE;
TYPE reverser(TYPE n)
{
TYPE k = 1, nrev = 0, i, nrevbit1, nrevbit2;
int count;
for(i = 0; !i || (1 << i && (1 << i) != 1); i+=2)
{
/*In each iteration, we swap one bit
on the 'right half' of the number with another
on the left half*/
k = 1<<i; /*this is used to find how many positions
to the left (or right, for the other bit)
we gotta move the bits in this iteration*/
count = 0;
while(k << 1 && k << 1 != 1)
{
k <<= 1;
count++;
}
nrevbit1 = n & (1<<(i/2));
nrevbit1 <<= count;
nrevbit2 = n & 1<<((i/2) + count);
nrevbit2 >>= count;
nrev |= nrevbit1;
nrev |= nrevbit2;
}
return nrev;
}
これは Windows 上の gcc では正常に動作しますが、プラットフォームに完全に依存しないかどうかはわかりません。懸念される箇所は次のとおりです。
for ループの条件 - 左端のビットを超えて 1 を左シフトすると、1 が「抜け落ち」た 0 (私が予想していたものであり、古き良き Turbo C が iirc で提供するもの) を含む 0 が得られるか、または1 が回ると 1 が得られます (gcc の動作と思われます)。
内側の while ループ内の条件:上記を参照。しかし、ここで奇妙なことが起こっています。この場合、gcc は 1 を丸めずに脱落させているようです。
コードは不可解であることが判明する可能性があります。興味があって説明が必要な場合は、遠慮せずに聞いてください。どこかに載せておきます。
@ΤΖΩΤΖΙΟΥ
ΤΖΩΤΖΙΟΥ さんのコメントに応えて、ビット幅の上限に依存する上記の修正版を提示します。
#include <stdio.h>
#include <stdint.h>
typedef int32_t TYPE;
TYPE reverse(TYPE x, int bits)
{
TYPE m=~0;
switch(bits)
{
case 64:
x = (x&0xFFFFFFFF00000000&m)>>16 | (x&0x00000000FFFFFFFF&m)<<16;
case 32:
x = (x&0xFFFF0000FFFF0000&m)>>16 | (x&0x0000FFFF0000FFFF&m)<<16;
case 16:
x = (x&0xFF00FF00FF00FF00&m)>>8 | (x&0x00FF00FF00FF00FF&m)<<8;
case 8:
x = (x&0xF0F0F0F0F0F0F0F0&m)>>4 | (x&0x0F0F0F0F0F0F0F0F&m)<<4;
x = (x&0xCCCCCCCCCCCCCCCC&m)>>2 | (x&0x3333333333333333&m)<<2;
x = (x&0xAAAAAAAAAAAAAAAA&m)>>1 | (x&0x5555555555555555&m)<<1;
}
return x;
}
int main(int argc, char**argv)
{
TYPE x;
TYPE b = (TYPE)-1;
int bits;
if ( argc != 2 )
{
printf("Usage: %s hexadecimal\n", argv[0]);
return 1;
}
for(bits=1;b;b<<=1,bits++);
--bits;
printf("TYPE has %d bits\n", bits);
sscanf(argv[1],"%x", &x);
printf("0x%x\n",reverse(x, bits));
return 0;
}
ノート:
- gcc は 64 ビット定数について警告します
- printfs も警告を生成します
- 64 ビット以上が必要な場合、コードは拡張できるほど単純である必要があります。
私が上記で犯したコーディング犯罪について、あらかじめお詫び申し上げます。ご容赦ください。
「ビットいじりのハック」の素晴らしいコレクションがあり、C でコーディングされたさまざまな単純なビット反転アルゴリズムとそれほど単純ではないビット反転アルゴリズムが含まれています。 http://graphics.stanford.edu/~seander/bithacks.html.
私は個人的に「Obvious」アルゴリズムが好きです(http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious) なぜなら、それは明らかだからです。他のものの中には、実行に必要な命令が少ないものもあります。本当に何かを最適化する必要がある場合は、それほど明白ではないが高速なバージョンを選択することがあります。それ以外の場合は、読みやすさ、保守性、移植性を考慮して、Obvious を選択します。
ここでは、より一般的に役立つバリエーションを示します。この利点は、反転される値 (コードワード) のビット長が不明であるが、maxLength と呼ばれる値を超えないことが保証されている状況でも機能することです。このケースの良い例は、ハフマン コードの圧縮解除です。
以下のコードは、長さが 1 ~ 24 ビットのコードワードで動作します。Pentium D 上で高速に実行できるように最適化されています。1 回の使用につきルックアップ テーブルに 3 回アクセスすることに注意してください。私は、より大きなテーブル (4096 エントリと 65,536 エントリ) を犠牲にして、その数を 2 に減らす多くのバリエーションを実験しました。256 バイトのテーブルを備えたこのバージョンが明らかに勝者でした。その理由の 1 つは、テーブル データがキャッシュ内にあることが非常に有利であるためであり、おそらくプロセッサーが 8 ビットのテーブル検索/変換命令を備えているためでもあります。
const unsigned char table[] = {
0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF};
const unsigned short masks[17] =
{0,0,0,0,0,0,0,0,0,0X0100,0X0300,0X0700,0X0F00,0X1F00,0X3F00,0X7F00,0XFF00};
unsigned long codeword; // value to be reversed, occupying the low 1-24 bits
unsigned char maxLength; // bit length of longest possible codeword (<= 24)
unsigned char sc; // shift count in bits and index into masks array
if (maxLength <= 8)
{
codeword = table[codeword << (8 - maxLength)];
}
else
{
sc = maxLength - 8;
if (maxLength <= 16)
{
codeword = (table[codeword & 0X00FF] << sc)
| table[codeword >> sc];
}
else if (maxLength & 1) // if maxLength is 17, 19, 21, or 23
{
codeword = (table[codeword & 0X00FF] << sc)
| table[codeword >> sc] |
(table[(codeword & masks[sc]) >> (sc - 8)] << 8);
}
else // if maxlength is 18, 20, 22, or 24
{
codeword = (table[codeword & 0X00FF] << sc)
| table[codeword >> sc]
| (table[(codeword & masks[sc]) >> (sc >> 1)] << (sc >> 1));
}
}
どうでしょうか:
long temp = 0;
int counter = 0;
int number_of_bits = sizeof(value) * 8; // get the number of bits that represent value (assuming that it is aligned to a byte boundary)
while(value > 0) // loop until value is empty
{
temp <<= 1; // shift whatever was in temp left to create room for the next bit
temp |= (value & 0x01); // get the lsb from value and set as lsb in temp
value >>= 1; // shift value right by one to look at next lsb
counter++;
}
value = temp;
if (counter < number_of_bits)
{
value <<= counter-number_of_bits;
}
(値が保持されるビット数がわかっており、それがnumber_of_bitsに格納されていると仮定しています)
明らかに、temp は想像できる限り最も長いデータ型である必要があり、temp を値にコピーして戻すと、temp 内のすべての無関係なビットが魔法のように消えるはずです (私はそう思います!)。
または、「c」の方法は次のように言います。
while(value)
あなたの選択
考えられるすべての 1 バイト シーケンスを反転した結果を配列 (256 個の異なるエントリ) に保存し、このテーブルへの検索と論理和演算ロジックを組み合わせて使用して、整数の反転を取得できます。
これはTKの解決策のバリエーションと修正であり、sundarによる解決策よりも明確である可能性があります。t から単一ビットを取得し、それらを return_val にプッシュします。
typedef unsigned long TYPE;
#define TYPE_BITS sizeof(TYPE)*8
TYPE reverser(TYPE t)
{
unsigned int i;
TYPE return_val = 0
for(i = 0; i < TYPE_BITS; i++)
{/*foreach bit in TYPE*/
/* shift the value of return_val to the left and add the rightmost bit from t */
return_val = (return_val << 1) + (t & 1);
/* shift off the rightmost bit of t */
t = t >> 1;
}
return(return_val);
}
任意のサイズの任意のタイプのオブジェクトに対して有効な一般的なアプローチは、オブジェクトのバイトを反転し、各バイトのビットの順序を反転することです。この場合、ビットレベルのアルゴリズムは具体的なビット数 (バイト) に関連付けられますが、「変数」ロジック (サイズに関する) はバイト全体のレベルに引き上げられます。
ここでは、freespace のソリューションを一般化して説明します (いつか 128 ビット マシンが登場した場合に備えて)。gcc -O3 でコンパイルするとジャンプのないコードが生成され、正常なマシンでは明らかに foo_t の定義の影響を受けません。残念ながら、それはシフトが 2 のべき乗であることに依存します。
#include <limits.h>
#include <stdio.h>
typedef unsigned long foo_t;
foo_t reverse(foo_t x)
{
int shift = sizeof (x) * CHAR_BIT / 2;
foo_t mask = (1 << shift) - 1;
int i;
for (i = 0; shift; i++) {
x = ((x & mask) << shift) | ((x & ~mask) >> shift);
shift >>= 1;
mask ^= (mask << shift);
}
return x;
}
int main() {
printf("reverse = 0x%08lx\n", reverse(0x12345678L));
}
ビット反転が時間的に重要であり、主に FFT と組み合わせて使用される場合、最善の方法はビット反転された配列全体を保存することです。いずれの場合も、この配列のサイズは、FFT Cooley-Tukey アルゴリズムで事前に計算する必要がある 1 の根よりも小さくなります。配列を計算する簡単な方法は次のとおりです。
int BitReverse[Size]; // Size is power of 2
void Init()
{
BitReverse[0] = 0;
for(int i = 0; i < Size/2; i++)
{
BitReverse[2*i] = BitReverse[i]/2;
BitReverse[2*i+1] = (BitReverse[i] + Size)/2;
}
} // end it's all