algorithm behind the generation of the reverse bits lookup table(8 bit)

后端 未结 3 455
余生分开走
余生分开走 2021-02-04 13:17

I found the lookup table here. The table is generated as a reverse bits table of 8 bits.

I can not figure out why it works. Please explain the theory behind it. Thanks<

相关标签:
3条回答
  • 2021-02-04 13:54

    Reverse bits table is just one of possible offline generated constants. People find an algorithm to define it by using unrolling macro. It won't be possible to find such algorithm for another constant. So you will have to maintain some generators infrastructure anyway.

    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #define BYTES_PER_LINE 16
    #define BYTES_GLUE ", "
    #define LINE_PREFIX "  "
    #define LINE_TERMINATOR ",\n"
    
    #define PRINT(string) fwrite(string, 1, sizeof(string), stdout)
    
    static inline void print_reversed_byte(uint8_t byte) {
      uint8_t reversed_byte = 0;
    
      for (uint8_t bit_index = 0; bit_index < 8; bit_index++) {
        uint8_t bit_value = (byte >> bit_index) & 1;
        reversed_byte |= bit_value << (7 - bit_index);
      }
    
      printf("0x%02x", reversed_byte);
    }
    
    int main() {
      uint8_t index = 0;
    
      while (true) {
        if (index != 0) {
          if (index % BYTES_PER_LINE == 0) {
            PRINT(LINE_TERMINATOR);
            PRINT(LINE_PREFIX);
          } else {
            PRINT(BYTES_GLUE);
          }
        }
    
        print_reversed_byte(index);
    
        if (index == 255) {
          break;
        }
        index++;
      }
    
      return 0;
    }
    

    Use it in generated_constants.c.in with cmake:

    const uint8_t REVERSE_BITS_TABLE[256] = {
      @CMAKE_REVERSE_BITS_TABLE@
    };
    

    You will receive pretty and compact table.

    See it's usage in LZWS for example.

    0 讨论(0)
  • 2021-02-04 13:56

    If you are working with Python and happen to end up here, this is how the lookup table would look like. Nevertheless, Bernd Elkemann's explanation still stands.

    # Generating the REVERSE_BIT_LUT while pre-processing
    # LUT is shorthand for lookuptable
    def R2(n, REVERSE_BIT_LUT):
        REVERSE_BIT_LUT.extend([n, n + 2 * 64, n + 1 * 64, n + 3 * 64])
    
    
    def R4(n, REVERSE_BIT_LUT):
        return (
            R2(n, REVERSE_BIT_LUT),
            R2(n + 2 * 16, REVERSE_BIT_LUT),
            R2(n + 1 * 16, REVERSE_BIT_LUT),
            R2(n + 3 * 16, REVERSE_BIT_LUT),
        )
    
    
    def R6(n, REVERSE_BIT_LUT):
        return (
            R4(n, REVERSE_BIT_LUT),
            R4(n + 2 * 4, REVERSE_BIT_LUT),
            R4(n + 1 * 4, REVERSE_BIT_LUT),
            R4(n + 3 * 4, REVERSE_BIT_LUT),
        )
    
    
    def LOOK_UP(REVERSE_BIT_LUT):
        return (
            R6(0, REVERSE_BIT_LUT),
            R6(2, REVERSE_BIT_LUT),
            R6(1, REVERSE_BIT_LUT),
            R6(3, REVERSE_BIT_LUT),
        )
    
    
    # LOOK_UP is the function to generate the REVERSE_BIT_LUT
    REVERSE_BIT_LUT = list()
    LOOK_UP(REVERSE_BIT_LUT)
    

    I am working on providing code snippets of Sean's bit hacks in Python here.

    0 讨论(0)
  • 2021-02-04 14:05

    First off a comment: This kind of thing is normally only done in the IOCCC. Code like this should not be used in production-environments because it is non-obvious. The reason why i mention this is to remove the false impression that this has any performance- or space benefit, the compiled code will contain the same (number of) bytes you would get if writing the 256 numbers directly into the array.

    Ok, now to how it works. It works recursively of course, defining two bits at a top level R6, then two more at the next... But how in detail? Ok:

    The first clue you get is the interesting 0->2->1->3 sequence. You should ask yourself "why?". This is the building block that is required for the construction. The numbers 0 1 2 3 in binary are 00 01 10 11 and if you reverse each: 00 10 01 11 which is 0 2 1 3!

    Now lets take a look at what we want the table to do: It should become something like this:

    00000000 10000000 01000000 11000000 
    00100000 10100000 01100000 11100000 
    00010000 10010000 01010000 11010000
    00110000 10110000 01110000 11110000 ...
    

    because you want it to map index 0 to 0, index 00000001 to 10000000 and so on.

    Notice that the most significant (leftmost) 2 bits of each number: 00 10 01 11 for every line!

    Now notice that the second most significant 2 bits of each number increase the same way (00 10 01 11) but for the "columns".

    The reason why i chose to order the array in rows of length 4 is, that we found out that 2 bits are written at a time and 2 bits can create 4 patterns.

    If you then continue observing the remaining numbers of the table (256 entries total) you will see that the 3rd 2 bits can be found having the 00 10 01 11 sequence if you order the table in columns of 16 and the last 2 bits when you order it in columns of 64.

    Now i implicitly told you where the numbers 16 and 64 in the original macro-expansion came from.

    That are the details, and to generalize: The highest level of the recursion generates the least significant 2 bits, the middle two levels do their thing and the lowest level generates the most significant 2 bits.

    0 讨论(0)
提交回复
热议问题