6

整数の 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( arg )を呼び出すと、ビットが反転されたargが返されます(バイナリ ツリーから取得されたバイナリ ツリーのキーとして使用されます)。カウンターを増やしますが、それは多かれ少なかれ無関係です)。

reverse_int_setup(); の呼び出し後にコンパイル時に max_int の正しい値を取得するプラットフォームに依存しない方法はありますか? それ以外の場合、 reverse_int() 用に私が持っているアルゴリズムよりも優れた/無駄のないアルゴリズムはありますか?

ありがとう。

4

12 に答える 12

6
#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の「ビット数」のアイデアを使用しましたが、バイトに8ビットが含まれていると想定せず、代わりにCHAR_BITマクロを使用することで、移植性を高めました。コードはより効率的になりました(内側のforループが削除されています)。今回は、コードの暗号化も少し少なくなることを願っています。:)

countを使用する必要があるのは、ビットをシフトする必要がある位置の数が反復ごとに異なることです。右端のビットを31位置(32ビット数と想定)、右から2番目のビットを29位置移動する必要があります。の上。したがって、iが増加するにつれて、反復ごとにカウントが減少する必要があります。

少しの情報がコードを理解するのに役立つことを願っています...

于 2008-09-16T10:06:42.217 に答える
6

次のプログラムは、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 が生成されました。

于 2008-09-15T16:50:28.587 に答える
3
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 を与えるもの)、または1 を回して 1 を取得します (gcc の動作のようです)。

  • 内側の while ループの条件: 上記を参照してください。しかし、ここで奇妙なことが起こっています: この場合、gcc は 1 を抜けさせ、回り込まないように見えます!

コードはわかりにくいかもしれません。興味があり、説明が必要な場合は、お気軽にお問い合わせください。どこかに掲載します。

于 2008-09-15T17:30:30.130 に答える
1

これは、より一般的に役立つバリエーションです。その利点は、反転する値のビット長(コードワード)が不明であるが、maxLengthと呼ばれる値を超えないことが保証されている状況で機能することです。この場合の良い例は、ハフマンコードの解凍です。

以下のコードは、長さが1〜24ビットのコードワードで機能します。PentiumDでの高速実行用に最適化されています。ルックアップテーブルには1回の使用で最大3回アクセスすることに注意してください。私は、より大きなテーブル(4096および65,536エントリ)を犠牲にして、その数を2に減らす多くのバリエーションを試しました。256バイトのテーブルを備えたこのバージョンは、テーブルデータがキャッシュにあることが非常に有利であり、プロセッサに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));  
   }  
}  
于 2009-12-04T05:36:40.530 に答える
1

http://graphics.stanford.edu/~seander/bithacks.htmlには、C でコーディングされたさまざまな単純なビット反転アルゴリズムやそれほど単純ではないビット反転アルゴリズムを含む、「Bit Twiddling Hacks」の優れたコレクションがあります。

私は個人的に「明白な」アルゴリズム ( http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious ) が好きです。他のいくつかは、実行するのに必要な命令が少ない場合があります。何かを本当に最適化する必要がある場合は、それほど明白ではないがより高速なバージョンを選択することがあります。それ以外の場合、読みやすさ、保守性、および移植性のために、私は明白なものを選択します。

于 2008-09-17T02:09:31.917 に答える
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ビット以上が必要な場合、コードは拡張できるほど単純でなければなりません

上記で犯したコーディング犯罪について、事前にお詫び申し上げます。

于 2008-09-16T16:39:20.820 に答える
0

一般的なアプローチは、任意のサイズの任意のタイプのオブジェクトに対して機能し、オブジェクトのバイトの順序を逆にし、各バイトのビットの順序を逆にします。この場合、ビットレベルのアルゴリズムは具体的なビット数(1バイト)に関連付けられ、「可変」ロジック(サイズに関して)はバイト全体のレベルに引き上げられます。

于 2009-12-04T05:44:04.947 に答える
0

これは、サンダーによるソリューションよりも明確な可能性があるTKのソリューションのバリエーションと修正です。t から 1 ビットを取得し、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);
}
于 2008-09-16T16:06:12.460 に答える
0

これが、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));
}
于 2011-02-18T01:38:20.283 に答える
0

ビット反転がタイム クリティカルであり、主に 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
于 2010-03-22T02:06:09.857 に答える
0

どうですか:

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)

あなたの選択

于 2008-09-15T15:17:51.447 に答える
0

可能なすべての 1 バイト シーケンスを反転した結果を配列 (256 の個別のエントリ) に格納し、このテーブルへのルックアップといくつかの論理和論理を組み合わせて整数の反転を取得できます。

于 2008-09-15T15:37:05.970 に答える