Bit reversal of an integer, ignoring integer size and endianness

后端 未结 12 2212
既然无缘
既然无缘 2021-01-03 00:18

Given an integer typedef:

typedef unsigned int TYPE;

or

typedef unsigned long TYPE;

I have the following

相关标签:
12条回答
  • 2021-01-03 00:44

    There's a nice collection of "Bit Twiddling Hacks", including a variety of simple and not-so simple bit reversing algorithms coded in C at http://graphics.stanford.edu/~seander/bithacks.html.

    I personally like the "Obvious" algorigthm (http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious) because, well, it's obvious. Some of the others may require less instructions to execute. If I really need to optimize the heck out of something I may choose the not-so-obvious but faster versions. Otherwise, for readability, maintainability, and portability I would choose the Obvious one.

    0 讨论(0)
  • 2021-01-03 00:44

    Here is a more generally useful variation. Its advantage is its ability to work in situations where the bit length of the value to be reversed -- the codeword -- is unknown but is guaranteed not to exceed a value we'll call maxLength. A good example of this case is Huffman code decompression.

    The code below works on codewords from 1 to 24 bits in length. It has been optimized for fast execution on a Pentium D. Note that it accesses the lookup table as many as 3 times per use. I experimented with many variations that reduced that number to 2 at the expense of a larger table (4096 and 65,536 entries). This version, with the 256-byte table, was the clear winner, partly because it is so advantageous for table data to be in the caches, and perhaps also because the processor has an 8-bit table lookup/translation instruction.

    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));  
       }  
    }  
    
    0 讨论(0)
  • 2021-01-03 00:46
    #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;
    }
    

    This time I've used the 'number of bits' idea from TK, but made it somewhat more portable by not assuming a byte contains 8 bits and instead using the CHAR_BIT macro. The code is more efficient now (with the inner for loop removed). I hope the code is also slightly less cryptic this time. :)

    The need for using count is that the number of positions by which we have to shift a bit varies in each iteration - we have to move the rightmost bit by 31 positions (assuming 32 bit number), the second rightmost bit by 29 positions and so on. Hence count must decrease with each iteration as i increases.

    Hope that bit of info proves helpful in understanding the code...

    0 讨论(0)
  • 2021-01-03 00:51
    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;
    }
    

    This works fine in gcc under Windows, but I'm not sure if it's completely platform independent. A few places of concern are:

    • the condition in the for loop - it assumes that when you left shift 1 beyond the leftmost bit, you get either a 0 with the 1 'falling out' (what I'd expect and what good old Turbo C gives iirc), or the 1 circles around and you get a 1 (what seems to be gcc's behaviour).

    • the condition in the inner while loop: see above. But there's a strange thing happening here: in this case, gcc seems to let the 1 fall out and not circle around!

    The code might prove cryptic: if you're interested and need an explanation please don't hesitate to ask - I'll put it up someplace.

    0 讨论(0)
  • 2021-01-03 00:53

    @ΤΖΩΤΖΙΟΥ

    In reply to ΤΖΩΤΖΙΟΥ 's comments, I present modified version of above which depends on a upper limit for bit width.

    #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;
    }
    

    Notes:

    • gcc will warn on the 64bit constants
    • the printfs will generate warnings too
    • If you need more than 64bit, the code should be simple enough to extend

    I apologise in advance for the coding crimes I committed above - mercy good sir!

    0 讨论(0)
  • 2021-01-03 00:54

    In case bit-reversal is time critical, and mainly in conjunction with FFT, the best is to store the whole bit reversed array. In any case, this array will be smaller in size than the roots of unity that have to be precomputed in FFT Cooley-Tukey algorithm. An easy way to compute the array is:

    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
    
    0 讨论(0)
提交回复
热议问题