it-swarm.com.de

Der effizienteste Algorithmus für die Bitumkehrung (von MSB-> LSB zu LSB-> MSB) in C

Was ist der beste Algorithmus, um Folgendes zu erreichen:

0010 0000 => 0000 0100

Die Konvertierung erfolgt von MSB → LSB nach LSB → MSB. Alle Bits müssen umgekehrt werden. das heißt, dies ist nicht endianness-swapping.

227
green_t

NOTE: Alle unten stehenden Algorithmen sind in C, sollten aber in die Sprache Ihrer Wahl portierbar sein (schauen Sie mich nur nicht an, wenn sie sind nicht so schnell :)

Optionen

Wenig Arbeitsspeicher (32-Bit int, 32-Bit-Computer) (von hier ):

unsigned int
reverse(register unsigned int x)
{
    x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
    x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
    x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
    x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
    return((x >> 16) | (x << 16));

}

Aus der berühmten Bit Twiddling Hacks Seite :

Schnellste (Nachschlagetabelle) :

static const unsigned char BitReverseTable256[] = 
{
  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
};

unsigned int v; // reverse 32-bit value, 8 bits at time
unsigned int c; // c will get v reversed

// Option 1:
c = (BitReverseTable256[v & 0xff] << 24) | 
    (BitReverseTable256[(v >> 8) & 0xff] << 16) | 
    (BitReverseTable256[(v >> 16) & 0xff] << 8) |
    (BitReverseTable256[(v >> 24) & 0xff]);

// Option 2:
unsigned char * p = (unsigned char *) &v;
unsigned char * q = (unsigned char *) &c;
q[3] = BitReverseTable256[p[0]]; 
q[2] = BitReverseTable256[p[1]]; 
q[1] = BitReverseTable256[p[2]]; 
q[0] = BitReverseTable256[p[3]];

Sie können diese Idee auf 64-Bit ints erweitern oder den Speicher gegen die Geschwindigkeit austauschen (vorausgesetzt, Ihr L1-Datencache ist groß genug) und jeweils 16 Bit mit einer 64K-Eintrags-Lookup-Tabelle umkehren.


Andere

Einfach

unsigned int v;     // input bits to be reversed
unsigned int r = v & 1; // r will be reversed bits of v; first get LSB of v
int s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end

for (v >>= 1; v; v >>= 1)
{   
  r <<= 1;
  r |= v & 1;
  s--;
}
r <<= s; // shift when v's highest bits are zero

Schneller (32-Bit-Prozessor)

unsigned char b = x;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; 

Schneller (64-Bit-Prozessor)

unsigned char b; // reverse this (8-bit) byte
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;

Wenn Sie dies für ein 32-Bit-int ausführen möchten, vertauschen Sie einfach die Bits in jedem Byte und kehren Sie die Reihenfolge der Bytes um. Das ist:

unsigned int toReverse;
unsigned int reversed;
unsigned char inByte0 = (toReverse & 0xFF);
unsigned char inByte1 = (toReverse & 0xFF00) >> 8;
unsigned char inByte2 = (toReverse & 0xFF0000) >> 16;
unsigned char inByte3 = (toReverse & 0xFF000000) >> 24;
reversed = (reverseBits(inByte0) << 24) | (reverseBits(inByte1) << 16) | (reverseBits(inByte2) << 8) | (reverseBits(inByte3);

Ergebnisse

Ich habe die beiden vielversprechendsten Lösungen, die Nachschlagetabelle und das bitweise UND (das erste), verglichen. Die Testmaschine ist ein Laptop mit 4 GB DDR2-800 und einem Core 2 Duo T7500 mit 2,4 GHz und 4 MB L2-Cache. YMMV. Ich habe gcc 4.3.2 unter 64-Bit-Linux verwendet. OpenMP (und die GCC-Bindungen) wurden für hochauflösende Timer verwendet.

reverse.c

#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

unsigned int
reverse(register unsigned int x)
{
    x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
    x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
    x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
    x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
    return((x >> 16) | (x << 16));

}

int main()
{
    unsigned int *ints = malloc(100000000*sizeof(unsigned int));
    unsigned int *ints2 = malloc(100000000*sizeof(unsigned int));
    for(unsigned int i = 0; i < 100000000; i++)
      ints[i] = Rand();

    unsigned int *inptr = ints;
    unsigned int *outptr = ints2;
    unsigned int *endptr = ints + 100000000;
    // Starting the time measurement
    double start = omp_get_wtime();
    // Computations to be measured
    while(inptr != endptr)
    {
      (*outptr) = reverse(*inptr);
      inptr++;
      outptr++;
    }
    // Measuring the elapsed time
    double end = omp_get_wtime();
    // Time calculation (in seconds)
    printf("Time: %f seconds\n", end-start);

    free(ints);
    free(ints2);

    return 0;
}

reverse_lookup.c

#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

static const unsigned char BitReverseTable256[] = 
{
  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
};

int main()
{
    unsigned int *ints = malloc(100000000*sizeof(unsigned int));
    unsigned int *ints2 = malloc(100000000*sizeof(unsigned int));
    for(unsigned int i = 0; i < 100000000; i++)
      ints[i] = Rand();

    unsigned int *inptr = ints;
    unsigned int *outptr = ints2;
    unsigned int *endptr = ints + 100000000;
    // Starting the time measurement
    double start = omp_get_wtime();
    // Computations to be measured
    while(inptr != endptr)
    {
    unsigned int in = *inptr;  

    // Option 1:
    //*outptr = (BitReverseTable256[in & 0xff] << 24) | 
    //    (BitReverseTable256[(in >> 8) & 0xff] << 16) | 
    //    (BitReverseTable256[(in >> 16) & 0xff] << 8) |
    //    (BitReverseTable256[(in >> 24) & 0xff]);

    // Option 2:
    unsigned char * p = (unsigned char *) &(*inptr);
    unsigned char * q = (unsigned char *) &(*outptr);
    q[3] = BitReverseTable256[p[0]]; 
    q[2] = BitReverseTable256[p[1]]; 
    q[1] = BitReverseTable256[p[2]]; 
    q[0] = BitReverseTable256[p[3]];

      inptr++;
      outptr++;
    }
    // Measuring the elapsed time
    double end = omp_get_wtime();
    // Time calculation (in seconds)
    printf("Time: %f seconds\n", end-start);

    free(ints);
    free(ints2);

    return 0;
}

Ich habe beide Ansätze bei verschiedenen Optimierungen ausprobiert, drei Versuche auf jeder Ebene durchgeführt und bei jedem Versuch 100 Millionen zufällige unsigned ints rückgängig gemacht. Für die Option Nachschlagetabelle habe ich beide Schemata (Optionen 1 und 2) ausprobiert, die auf der Seite für bitweise Hacks angegeben sind. Die Ergebnisse sind unten gezeigt.

Bitweises UND

[email protected]:~/code$ gcc -fopenmp -std=c99 -o reverse reverse.c
[email protected]:~/code$ ./reverse
Time: 2.000593 seconds
[email protected]:~/code$ ./reverse
Time: 1.938893 seconds
[email protected]:~/code$ ./reverse
Time: 1.936365 seconds
[email protected]:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse reverse.c
[email protected]:~/code$ ./reverse
Time: 0.942709 seconds
[email protected]:~/code$ ./reverse
Time: 0.991104 seconds
[email protected]:~/code$ ./reverse
Time: 0.947203 seconds
[email protected]:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse reverse.c
[email protected]:~/code$ ./reverse
Time: 0.922639 seconds
[email protected]:~/code$ ./reverse
Time: 0.892372 seconds
[email protected]:~/code$ ./reverse
Time: 0.891688 seconds

Nachschlagetabelle (Option 1)

[email protected]:~/code$ gcc -fopenmp -std=c99 -o reverse_lookup reverse_lookup.c
[email protected]:~/code$ ./reverse_lookup
Time: 1.201127 seconds              
[email protected]:~/code$ ./reverse_lookup
Time: 1.196129 seconds              
[email protected]:~/code$ ./reverse_lookup
Time: 1.235972 seconds              
[email protected]:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse_lookup reverse_lookup.c
[email protected]:~/code$ ./reverse_lookup
Time: 0.633042 seconds              
[email protected]:~/code$ ./reverse_lookup
Time: 0.655880 seconds              
[email protected]:~/code$ ./reverse_lookup
Time: 0.633390 seconds              
[email protected]:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse_lookup reverse_lookup.c
[email protected]:~/code$ ./reverse_lookup
Time: 0.652322 seconds              
[email protected]:~/code$ ./reverse_lookup
Time: 0.631739 seconds              
[email protected]:~/code$ ./reverse_lookup
Time: 0.652431 seconds  

Nachschlagetabelle (Option 2)

[email protected]:~/code$ gcc -fopenmp -std=c99 -o reverse_lookup reverse_lookup.c
[email protected]:~/code$ ./reverse_lookup
Time: 1.671537 seconds
[email protected]:~/code$ ./reverse_lookup
Time: 1.688173 seconds
[email protected]:~/code$ ./reverse_lookup
Time: 1.664662 seconds
[email protected]:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse_lookup reverse_lookup.c
[email protected]:~/code$ ./reverse_lookup
Time: 1.049851 seconds
[email protected]:~/code$ ./reverse_lookup
Time: 1.048403 seconds
[email protected]:~/code$ ./reverse_lookup
Time: 1.085086 seconds
[email protected]:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse_lookup reverse_lookup.c
[email protected]:~/code$ ./reverse_lookup
Time: 1.082223 seconds
[email protected]:~/code$ ./reverse_lookup
Time: 1.053431 seconds
[email protected]:~/code$ ./reverse_lookup
Time: 1.081224 seconds

Fazit

Verwenden Sie die Nachschlagetabelle mit Option 1 (die Byteadressierung ist nicht überraschend langsam), wenn Sie sich Gedanken über die Leistung machen. Wenn Sie das letzte Byte des Arbeitsspeichers aus Ihrem System herausholen müssen (und wenn Sie sich für die Leistung der Bitumkehrung interessieren), sind die optimierten Versionen des bitweisen UND-Ansatzes auch nicht allzu schäbig.

Vorbehalt

Ja, ich weiß, dass der Benchmark-Code ein vollständiger Hack ist. Vorschläge zur Verbesserung sind mehr als willkommen. Dinge, die ich kenne:

  • Ich habe keinen Zugang zu ICC. Dies kann schneller sein (bitte antworten Sie in einem Kommentar, wenn Sie dies testen können).
  • Eine 64-KByte-Nachschlagetabelle eignet sich möglicherweise gut für einige moderne Mikroarchitekturen mit großer L1D.
  • -mtune = native hat für -O2/-O3 nicht funktioniert (ld hat einen verrückten Fehler bei der Neudefinition von Symbolen verursacht), daher glaube ich nicht, dass der generierte Code für meine Mikroarchitektur optimiert ist.
  • Möglicherweise gibt es eine Möglichkeit, dies mit SSE etwas schneller durchzuführen. Ich habe keine Ahnung, wie, aber mit der schnellen Replikation, bitweise UND-gepackt und Swizzling-Anweisungen, muss es etwas geben.
  • Ich kenne nur genug x86-Assembly, um gefährlich zu sein; Hier ist der Code, den GCC auf -O3 für Option 1 generiert hat.

32-Bit

.L3:
movl    (%r12,%rsi), %ecx
movzbl  %cl, %eax
movzbl  BitReverseTable256(%rax), %edx
movl    %ecx, %eax
shrl    $24, %eax
mov     %eax, %eax
movzbl  BitReverseTable256(%rax), %eax
sall    $24, %edx
orl     %eax, %edx
movzbl  %ch, %eax
shrl    $16, %ecx
movzbl  BitReverseTable256(%rax), %eax
movzbl  %cl, %ecx
sall    $16, %eax
orl     %eax, %edx
movzbl  BitReverseTable256(%rcx), %eax
sall    $8, %eax
orl     %eax, %edx
movl    %edx, (%r13,%rsi)
addq    $4, %rsi
cmpq    $400000000, %rsi
jne     .L3

BEARBEITEN: Ich habe auch versucht, uint64_t -Typen auf meinem Computer zu verwenden, um festzustellen, ob es Leistungssteigerungen gab. Die Leistung war etwa 10% schneller als bei 32-Bit-Versionen und nahezu identisch, unabhängig davon, ob Sie nur 64-Bit-Typen zum Umkehren von Bits auf zwei 32-Bit-int -Typen gleichzeitig verwendet haben oder ob Sie tatsächlich Bits in umgekehrt haben halb so viele 64-Bit-Werte. Der Assembly-Code ist unten dargestellt (im ersten Fall werden die Bits für jeweils zwei 32-Bit-int -Typen umgekehrt):

.L3:
movq    (%r12,%rsi), %rdx
movq    %rdx, %rax
shrq    $24, %rax
andl    $255, %eax
movzbl  BitReverseTable256(%rax), %ecx
movzbq  %dl,%rax
movzbl  BitReverseTable256(%rax), %eax
salq    $24, %rax
orq     %rax, %rcx
movq    %rdx, %rax
shrq    $56, %rax
movzbl  BitReverseTable256(%rax), %eax
salq    $32, %rax
orq     %rax, %rcx
movzbl  %dh, %eax
shrq    $16, %rdx
movzbl  BitReverseTable256(%rax), %eax
salq    $16, %rax
orq     %rax, %rcx
movzbq  %dl,%rax
shrq    $16, %rdx
movzbl  BitReverseTable256(%rax), %eax
salq    $8, %rax
orq     %rax, %rcx
movzbq  %dl,%rax
shrq    $8, %rdx
movzbl  BitReverseTable256(%rax), %eax
salq    $56, %rax
orq     %rax, %rcx
movzbq  %dl,%rax
shrq    $8, %rdx
movzbl  BitReverseTable256(%rax), %eax
andl    $255, %edx
salq    $48, %rax
orq     %rax, %rcx
movzbl  BitReverseTable256(%rdx), %eax
salq    $40, %rax
orq     %rax, %rcx
movq    %rcx, (%r13,%rsi)
addq    $8, %rsi
cmpq    $400000000, %rsi
jne     .L3
489
Matt J

Dieser Thread erregte meine Aufmerksamkeit, da es sich um ein einfaches Problem handelt, das selbst für eine moderne CPU sehr viel Arbeit (CPU-Zyklen) erfordert. Und eines Tages stand ich auch mit demselben ¤ #% "#" - Problem da. Ich musste Millionen Bytes umdrehen. Ich weiß jedoch, dass alle meine Zielsysteme auf modernen Intel-basierten Systemen basieren.

Also habe ich den Lookup-Code von Matt J als Basis verwendet. Das System, an dem ich Benchmarking durchführe, ist ein i7 haswell 4700eq.

Matt Js Lookup flippt 400 000 000 Bytes: ca. 0,272 Sekunden.

Ich fuhr dann fort und versuchte zu sehen, ob der ISPC-Compiler von Intel die Arithmetik in reverse.c vektorisieren konnte.

Ich werde Sie mit meinen Erkenntnissen hier nicht langweilen, da ich viel versucht habe, den Compiler bei der Suche nach Material zu unterstützen. Trotzdem hatte ich eine Performance von etwa 0,15 Sekunden, um 400.000.000 Byte zu flippen. Das ist eine große Reduzierung, aber für meine Anwendung ist das immer noch viel zu langsam.

Die Leute ließen mich also den schnellsten auf Intel basierenden Bitflipper der Welt präsentieren. Getaktet um:

Zeit zum Bitflip 400000000 Bytes: 0,050082 Sekunden !!!!! 

// Bitflip using AVX2 - The fastest Intel based bitflip in the world!!
// Made by Anders Cedronius 2014 (anders.cedronius (you know what) gmail.com)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>

using namespace std;

#define DISPLAY_HEIGHT  4
#define DISPLAY_WIDTH   32
#define NUM_DATA_BYTES  400000000

// Constants (first we got the mask, then the high order nibble look up table and last we got the low order nibble lookup table)
__attribute__ ((aligned(32))) static unsigned char k1[32*3]={
        0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
        0x00,0x08,0x04,0x0c,0x02,0x0a,0x06,0x0e,0x01,0x09,0x05,0x0d,0x03,0x0b,0x07,0x0f,0x00,0x08,0x04,0x0c,0x02,0x0a,0x06,0x0e,0x01,0x09,0x05,0x0d,0x03,0x0b,0x07,0x0f,
        0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0
};

// The data to be bitflipped (+32 to avoid the quantization out of memory problem)
__attribute__ ((aligned(32))) static unsigned char data[NUM_DATA_BYTES+32]={};

extern "C" {
void bitflipbyte(unsigned char[],unsigned int,unsigned char[]);
}

int main()
{

    for(unsigned int i = 0; i < NUM_DATA_BYTES; i++)
    {
        data[i] = Rand();
    }

    printf ("\r\nData in(start):\r\n");
    for (unsigned int j = 0; j < 4; j++)
    {
        for (unsigned int i = 0; i < DISPLAY_WIDTH; i++)
        {
            printf ("0x%02x,",data[i+(j*DISPLAY_WIDTH)]);
        }
        printf ("\r\n");
    }

    printf ("\r\nNumber of 32-byte chunks to convert: %d\r\n",(unsigned int)ceil(NUM_DATA_BYTES/32.0));

    double start_time = omp_get_wtime();
    bitflipbyte(data,(unsigned int)ceil(NUM_DATA_BYTES/32.0),k1);
    double end_time = omp_get_wtime();

    printf ("\r\nData out:\r\n");
    for (unsigned int j = 0; j < 4; j++)
    {
        for (unsigned int i = 0; i < DISPLAY_WIDTH; i++)
        {
            printf ("0x%02x,",data[i+(j*DISPLAY_WIDTH)]);
        }
        printf ("\r\n");
    }
    printf("\r\n\r\nTime to bitflip %d bytes: %f seconds\r\n\r\n",NUM_DATA_BYTES, end_time-start_time);

    // return with no errors
    return 0;
}

Die printf's sind zum Debuggen.

Hier ist das Arbeitstier:

bits 64
global bitflipbyte

bitflipbyte:    
        vmovdqa     ymm2, [rdx]
        add         rdx, 20h
        vmovdqa     ymm3, [rdx]
        add         rdx, 20h
        vmovdqa     ymm4, [rdx]
bitflipp_loop:
        vmovdqa     ymm0, [rdi] 
        vpand       ymm1, ymm2, ymm0 
        vpandn      ymm0, ymm2, ymm0 
        vpsrld      ymm0, ymm0, 4h 
        vpshufb     ymm1, ymm4, ymm1 
        vpshufb     ymm0, ymm3, ymm0         
        vpor        ymm0, ymm0, ymm1
        vmovdqa     [rdi], ymm0
        add     rdi, 20h
        dec     rsi
        jnz     bitflipp_loop
        ret

Der Code nimmt 32 Bytes und maskiert dann die Nibbles. Das hohe Halbbyte wird um 4 nach rechts verschoben. Dann benutze ich vpshufb und ymm4/ymm3 als Nachschlagetabellen. Ich könnte eine einzelne Nachschlagetabelle verwenden, aber dann müsste ich nach links verschieben, bevor die Nibbles wieder zusammengebastelt werden.

Es gibt noch schnellere Möglichkeiten, die Bits zu spiegeln. Aber ich bin an Single-Thread und CPU gebunden, also war dies das schnellste was ich erreichen konnte. Kannst du eine schnellere Version machen?

Bitte machen Sie keine Kommentare zur Verwendung der Befehle "Intel C/C++ Compiler Intrinsic Equivalent" ...

68

Dies ist eine weitere Lösung für Leute, die Rekursion lieben.

Die Idee ist einfach. Teilen Sie den Eingang durch die Hälfte und tauschen Sie die beiden Hälften aus. Fahren Sie fort, bis das Einzelbit erreicht wird.

Illustrated in the example below.

Ex : If Input is 00101010   ==> Expected output is 01010100

1. Divide the input into 2 halves 
    0010 --- 1010

2. Swap the 2 Halves
    1010     0010

3. Repeat the same for each half.
    10 -- 10 ---  00 -- 10
    10    10      10    00

    1-0 -- 1-0 --- 1-0 -- 0-0
    0 1    0 1     0 1    0 0

Done! Output is 01010100

Hier ist eine rekursive Funktion, um es zu lösen. (Hinweis: Ich habe vorzeichenlose Ints verwendet, so dass es für Eingaben bis zu sizeof (unsigned int) * 8 Bit funktionieren kann.

Die rekursive Funktion benötigt 2 Parameter - den Wert, dessen Bits .__ benötigen. umzukehren und die Anzahl der Bits im Wert.

int reverse_bits_recursive(unsigned int num, unsigned int numBits)
{
    unsigned int reversedNum;;
    unsigned int mask = 0;

    mask = (0x1 << (numBits/2)) - 1;

    if (numBits == 1) return num;
    reversedNum = reverse_bits_recursive(num >> numBits/2, numBits/2) |
                   reverse_bits_recursive((num & mask), numBits/2) << numBits/2;
    return reversedNum;
}

int main()
{
    unsigned int reversedNum;
    unsigned int num;

    num = 0x55;
    reversedNum = reverse_bits_recursive(num, 8);
    printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);

    num = 0xabcd;
    reversedNum = reverse_bits_recursive(num, 16);
    printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);

    num = 0x123456;
    reversedNum = reverse_bits_recursive(num, 24);
    printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);

    num = 0x11223344;
    reversedNum = reverse_bits_recursive(num,32);
    printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
}

Dies ist die Ausgabe:

Bit Reversal Input = 0x55 Output = 0xaa
Bit Reversal Input = 0xabcd Output = 0xb3d5
Bit Reversal Input = 0x123456 Output = 0x651690
Bit Reversal Input = 0x11223344 Output = 0x22cc4488
13
Dennis Mathews

Nun, das ist sicherlich keine Antwort wie bei Matt J, aber hoffentlich wird es trotzdem nützlich sein.

size_t reverse(size_t n, unsigned int bytes)
{
    __asm__("BSWAP %0" : "=r"(n) : "0"(n));
    n >>= ((sizeof(size_t) - bytes) * 8);
    n = ((n & 0xaaaaaaaaaaaaaaaa) >> 1) | ((n & 0x5555555555555555) << 1);
    n = ((n & 0xcccccccccccccccc) >> 2) | ((n & 0x3333333333333333) << 2);
    n = ((n & 0xf0f0f0f0f0f0f0f0) >> 4) | ((n & 0x0f0f0f0f0f0f0f0f) << 4);
    return n;
}

Dies ist genau die gleiche Idee wie der beste Algorithmus von Matt, außer dass es diesen kleinen Befehl namens BSWAP gibt, der die Bytes (nicht die Bits) einer 64-Bit-Zahl austauscht. B7, b6, b5, b4, b3, b2, b1, b0 wird b0, b1, b2, b3, b4, b5, b6, b7. Da wir mit einer 32-Bit-Zahl arbeiten, müssen wir unsere byte-swapped-Zahl um 32 Bit nach unten verschieben. Dies lässt uns nur die Aufgabe, die 8 Bits jedes Bytes zu tauschen, was erledigt ist und voila! Wir sind fertig.

Timing: Auf meinem Rechner lief der Algorithmus von Matt ~ 0,52 Sekunden pro Versuch. Mein Versuch dauerte etwa 0,42 Sekunden pro Versuch. 20% schneller ist nicht schlecht finde ich.

Wenn Sie sich Sorgen machen, ob die Anweisung BSWAP Wikipedia verfügbar ist, wird die Anweisung BSWAP mit 80846 hinzugefügt, die 1989 veröffentlicht wurde. Es wird darauf hingewiesen, dass Wikipedia auch angibt, dass diese Anweisung nur für 32-Bit-Register funktioniert, die ist auf meinem Rechner eindeutig nicht der Fall, es funktioniert sehr viel nur auf 64-Bit-Registern.

Diese Methode funktioniert gleichermaßen für jeden integrierten Datentyp, sodass die Methode durch die Übergabe der gewünschten Anzahl von Bytes trivialisiert werden kann:

    size_t reverse(size_t n, unsigned int bytes)
    {
        __asm__("BSWAP %0" : "=r"(n) : "0"(n));
        n >>= ((sizeof(size_t) - bytes) * 8);
        n = ((n & 0xaaaaaaaaaaaaaaaa) >> 1) | ((n & 0x5555555555555555) << 1);
        n = ((n & 0xcccccccccccccccc) >> 2) | ((n & 0x3333333333333333) << 2);
        n = ((n & 0xf0f0f0f0f0f0f0f0) >> 4) | ((n & 0x0f0f0f0f0f0f0f0f) << 4);
        return n;
    }

das kann dann aufgerufen werden wie:

    n = reverse(n, sizeof(char));//only reverse 8 bits
    n = reverse(n, sizeof(short));//reverse 16 bits
    n = reverse(n, sizeof(int));//reverse 32 bits
    n = reverse(n, sizeof(size_t));//reverse 64 bits

Der Compiler sollte in der Lage sein, die zusätzlichen Parameter weg zu optimieren (unter der Annahme, dass der Compiler die Funktion einordnet), und für den Fall sizeof(size_t) würde die Rechtsverschiebung vollständig entfernt. Beachten Sie, dass der GCC zumindest nicht in der Lage ist, BSWAP zu entfernen und nach sizeof(char) nach rechts zu verschieben.

11
SirGuy

Anders Cedronius's Antwort bietet eine großartige Lösung für Benutzer, die eine x86-CPU mit AVX2-Unterstützung haben. Für x86-Plattformen ohne AVX-Unterstützung oder Nicht-x86-Plattformen sollte eine der folgenden Implementierungen gut funktionieren.

Der erste Code ist eine Variante der klassischen binären Partitionierungsmethode, die so codiert ist, dass die Verwendung des Shift-Plus-Logic-Idioms auf verschiedenen ARM -Prozessoren maximiert wird. Darüber hinaus wird die On-The-Fly-Maskengenerierung verwendet, die für RISC-Prozessoren von Vorteil sein könnte, die ansonsten mehrere Anweisungen zum Laden jedes 32-Bit-Maskenwerts benötigen. Compiler für x86-Plattformen sollten eine konstante Ausbreitung verwenden, um alle Masken zur Kompilierungszeit und nicht zur Laufzeit zu berechnen.

/* Classic binary partitioning algorithm */
inline uint32_t brev_classic (uint32_t a)
{
    uint32_t m;
    a = (a >> 16) | (a << 16);                            // swap halfwords
    m = 0x00ff00ff; a = ((a >> 8) & m) | ((a << 8) & ~m); // swap bytes
    m = m^(m << 4); a = ((a >> 4) & m) | ((a << 4) & ~m); // swap nibbles
    m = m^(m << 2); a = ((a >> 2) & m) | ((a << 2) & ~m);
    m = m^(m << 1); a = ((a >> 1) & m) | ((a << 1) & ~m);
    return a;
}

In Band 4A von "The Art of Computer Programming" zeigt D. Knuth clevere Methoden zum Umkehren von Bits, die überraschenderweise weniger Operationen erfordern als die klassischen binären Partitionierungsalgorithmen. Ein solcher Algorithmus für 32-Bit-Operanden, den ich in TAOCP nicht finden kann, ist in diesem Dokument auf der Hacker's Delight-Website dargestellt.

/* Knuth's algorithm from http://www.hackersdelight.org/revisions.pdf. Retrieved 8/19/2015 */
inline uint32_t brev_knuth (uint32_t a)
{
    uint32_t t;
    a = (a << 15) | (a >> 17);
    t = (a ^ (a >> 10)) & 0x003f801f; 
    a = (t + (t << 10)) ^ a;
    t = (a ^ (a >>  4)) & 0x0e038421; 
    a = (t + (t <<  4)) ^ a;
    t = (a ^ (a >>  2)) & 0x22488842; 
    a = (t + (t <<  2)) ^ a;
    return a;
}

Unter Verwendung des C/C++ - Compilers 13.1.3.198 des Intel-Compilers werden beide oben genannten Funktionen für die automatische Vektorisierung von XMM-Registern verwendet. Sie können auch ohne großen Aufwand manuell vektorisiert werden.

Bei meinem IvyBridge Xeon E3 1270v2 wurden unter Verwendung des automatisch vektorisierten Codes 100 Millionen uin32_t-Wörter in 0,070 Sekunden mit brev_classic() und 0,068 Sekunden mit brev_knuth() bit-reversiert. Ich habe darauf geachtet, dass mein Benchmark nicht durch die Bandbreite des Systemspeichers begrenzt ist.

10
njuffa

Angenommen, Sie haben ein Array von Bits, wie wäre es damit: 1. Schieben Sie Bits von MSB eins nach dem anderen in einen Stapel . 2. Platzieren Sie Bits aus diesem Stapel in ein anderes Array (oder dasselbe Array, wenn Sie Speicherplatz sparen möchten), platzieren Sie das erste Popping-Bit in MSB und gehen Sie von dort zu den weniger signifikanten Bits über.

Stack stack = new Stack();
Bit[] bits = new Bit[] { 0, 0, 1, 0, 0, 0, 0, 0 };

for (int i = 0; i < bits.Length; i++) 
{
    stack.Push(bits[i]);
}

for (int i = 0; i < bits.Length; i++)
{
    bits[i] = stack.pop();
}
8

Dies ist kein Job für einen Menschen!... aber perfekt für eine Maschine

Dies ist das Jahr 2015, 6 Jahre, seitdem diese Frage zum ersten Mal gestellt wurde. Compiler sind seitdem zu unseren Meistern geworden, und unsere Aufgabe als Mensch besteht nur darin, ihnen zu helfen. Was ist also der beste Weg, um unsere Absichten an die Maschine weiterzugeben?

Die Bit-Umkehrung ist so häufig, dass Sie sich fragen müssen, warum die x86 ständig wächst ISA keine Anweisung enthält, es auf einmal zu tun.

Der Grund: Wenn Sie dem Compiler Ihre eigentliche prägnante Absicht geben, sollte die Bitumkehr nur ~ 20 CPU-Zyklen erfordern. Lassen Sie mich Ihnen zeigen, wie Sie reverse () basteln und verwenden:

#include <inttypes.h>
#include <stdio.h>

uint64_t reverse(const uint64_t n,
                 const uint64_t k)
{
        uint64_t r, i;
        for (r = 0, i = 0; i < k; ++i)
                r |= ((n >> i) & 1) << (k - i - 1);
        return r;
}

int main()
{
        const uint64_t size = 64;
        uint64_t sum = 0;
        uint64_t a;
        for (a = 0; a < (uint64_t)1 << 30; ++a)
                sum += reverse(a, size);
        printf("%" PRIu64 "\n", sum);
        return 0;
}

Wenn Sie dieses Beispielprogramm mit der Clang-Version> = 3.6, -O3, -march = native (mit Haswell getestet) kompilieren, erhalten Sie mit den neuen AVX2-Anweisungen Code für die Bildqualität, mit einer Laufzeit von 11 Sekunden () s. Das sind ~ 10 ns pro Umkehrung (), mit 0,5 ns CPU-Zyklus unter der Annahme, dass 2 GHz bei satten 20 CPU-Zyklen liegt.

  • Sie können 10 reverse () s in der Zeit einbauen, die ein einmaliger Zugriff auf RAM einmal für ein einzelnes großes Array erforderlich ist.
  • Sie können 1 reverse () in der Zeit anpassen, die für den zweimaligen Zugriff auf eine L2-Cache-LUT erforderlich ist.

Vorbehalt: Dieser Beispielcode sollte für ein paar Jahre als anständiger Benchmark gelten, er wird jedoch schließlich sein Alter zeigen, sobald Compiler intelligent genug sind, um main () so zu optimieren, dass nur das Endergebnis gedruckt wird, anstatt wirklich etwas zu berechnen. Im Moment funktioniert es jedoch mit Reverse ().

6
user2875414

Ich weiß, dass es nicht C ist, sondern asm:

var1 dw 0f0f0
clc
     Push ax
     Push cx
     mov cx 16
loop1:
     shl var1
     shr ax
loop loop1
     pop ax
     pop cx

Dies funktioniert mit dem Übertragsbit, so dass Sie auch Flags speichern können

5
Coco

Natürlich ist die offensichtliche Quelle von bit-twiddling-Hacks hier: http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious

5
Anders Hansson

Die native ARM - Anweisung "rbit" kann dies mit einem CPU-Zyklus und einem zusätzlichen CPU-Register, das unmöglich zu schlagen ist.

5
metalogic

Implementierung mit wenig Arbeitsspeicher und schnellsten. 

private Byte  BitReverse(Byte bData)
    {
        Byte[] lookup = { 0, 8,  4, 12, 
                          2, 10, 6, 14 , 
                          1, 9,  5, 13,
                          3, 11, 7, 15 };
        Byte ret_val = (Byte)(((lookup[(bData & 0x0F)]) << 4) + lookup[((bData & 0xF0) >> 4)]);
        return ret_val;
    }
4
Aung

Nun, dies ist im Grunde dasselbe wie beim ersten "reverse ()", aber es ist 64 Bit und benötigt nur eine direkte Maske, um aus dem Befehlsstrom geladen zu werden. GCC erstellt Code ohne Sprünge, daher sollte dies ziemlich schnell sein.

#include <stdio.h>

static unsigned long long swap64(unsigned long long val)
{
#define ZZZZ(x,s,m) (((x) >>(s)) & (m)) | (((x) & (m))<<(s));
/* val = (((val) >>16) & 0xFFFF0000FFFF) | (((val) & 0xFFFF0000FFFF)<<16); */

val = ZZZZ(val,32,  0x00000000FFFFFFFFull );
val = ZZZZ(val,16,  0x0000FFFF0000FFFFull );
val = ZZZZ(val,8,   0x00FF00FF00FF00FFull );
val = ZZZZ(val,4,   0x0F0F0F0F0F0F0F0Full );
val = ZZZZ(val,2,   0x3333333333333333ull );
val = ZZZZ(val,1,   0x5555555555555555ull );

return val;
#undef ZZZZ
}

int main(void)
{
unsigned long long val, aaaa[16] =
 { 0xfedcba9876543210,0xedcba9876543210f,0xdcba9876543210fe,0xcba9876543210fed
 , 0xba9876543210fedc,0xa9876543210fedcb,0x9876543210fedcba,0x876543210fedcba9
 , 0x76543210fedcba98,0x6543210fedcba987,0x543210fedcba9876,0x43210fedcba98765
 , 0x3210fedcba987654,0x210fedcba9876543,0x10fedcba98765432,0x0fedcba987654321
 };
unsigned iii;

for (iii=0; iii < 16; iii++) {
    val = swap64 (aaaa[iii]);
    printf("A[]=%016llX Sw=%016llx\n", aaaa[iii], val);
    }
return 0;
}
3
wildplasser

Möglicherweise möchten Sie die Standardvorlagenbibliothek verwenden. Es kann langsamer sein als der oben genannte Code. Es scheint mir jedoch klarer und verständlicher zu sein. 

 #include<bitset>
 #include<iostream>


 template<size_t N>
 const std::bitset<N> reverse(const std::bitset<N>& ordered)
 {
      std::bitset<N> reversed;
      for(size_t i = 0, j = N - 1; i < N; ++i, --j)
           reversed[j] = ordered[i];
      return reversed;
 };


 // test the function
 int main()
 {
      unsigned long num; 
      const size_t N = sizeof(num)*8;

      std::cin >> num;
      std::cout << std::showbase << std::hex;
      std::cout << "ordered  = " << num << std::endl;
      std::cout << "reversed = " << reverse<N>(num).to_ulong()  << std::endl;
      std::cout << "double_reversed = " << reverse<N>(reverse<N>(num)).to_ulong() << std::endl;  
 }
3
Cem

Ich war gespannt, wie schnell die offensichtliche rohe Rotation sein würde.__ Auf meiner Maschine (i7 @ 2600) lag der Durchschnitt für 1.500.150.000 Iterationen 27.28 ns (über eine zufällige Menge von 131.071 64-Bit-Ganzzahlen).

Vorteile: Der Speicherbedarf ist gering und der Code ist einfach. Ich würde sagen, es ist auch nicht so groß. Die erforderliche Zeit ist für jede Eingabe vorhersehbar und konstant (128 arithmetische UMSCHALT-Operationen + 64 logische UND-Operationen + 64 logische OR -Operationen).

Ich verglich die beste Zeit von @Matt J - wer hat die akzeptierte Antwort. Wenn ich seine Antwort richtig gelesen habe, war das Beste, was er bekam, 0.631739 Sekunden für 1,000,000 Iterationen, was zu einem Durchschnitt von 631 ns pro Umdrehung führt.

Der Code-Ausschnitt, den ich verwendet habe, ist der folgende:

unsigned long long reverse_long(unsigned long long x)
{
    return (((x >> 0) & 1) << 63) |
           (((x >> 1) & 1) << 62) |
           (((x >> 2) & 1) << 61) |
           (((x >> 3) & 1) << 60) |
           (((x >> 4) & 1) << 59) |
           (((x >> 5) & 1) << 58) |
           (((x >> 6) & 1) << 57) |
           (((x >> 7) & 1) << 56) |
           (((x >> 8) & 1) << 55) |
           (((x >> 9) & 1) << 54) |
           (((x >> 10) & 1) << 53) |
           (((x >> 11) & 1) << 52) |
           (((x >> 12) & 1) << 51) |
           (((x >> 13) & 1) << 50) |
           (((x >> 14) & 1) << 49) |
           (((x >> 15) & 1) << 48) |
           (((x >> 16) & 1) << 47) |
           (((x >> 17) & 1) << 46) |
           (((x >> 18) & 1) << 45) |
           (((x >> 19) & 1) << 44) |
           (((x >> 20) & 1) << 43) |
           (((x >> 21) & 1) << 42) |
           (((x >> 22) & 1) << 41) |
           (((x >> 23) & 1) << 40) |
           (((x >> 24) & 1) << 39) |
           (((x >> 25) & 1) << 38) |
           (((x >> 26) & 1) << 37) |
           (((x >> 27) & 1) << 36) |
           (((x >> 28) & 1) << 35) |
           (((x >> 29) & 1) << 34) |
           (((x >> 30) & 1) << 33) |
           (((x >> 31) & 1) << 32) |
           (((x >> 32) & 1) << 31) |
           (((x >> 33) & 1) << 30) |
           (((x >> 34) & 1) << 29) |
           (((x >> 35) & 1) << 28) |
           (((x >> 36) & 1) << 27) |
           (((x >> 37) & 1) << 26) |
           (((x >> 38) & 1) << 25) |
           (((x >> 39) & 1) << 24) |
           (((x >> 40) & 1) << 23) |
           (((x >> 41) & 1) << 22) |
           (((x >> 42) & 1) << 21) |
           (((x >> 43) & 1) << 20) |
           (((x >> 44) & 1) << 19) |
           (((x >> 45) & 1) << 18) |
           (((x >> 46) & 1) << 17) |
           (((x >> 47) & 1) << 16) |
           (((x >> 48) & 1) << 15) |
           (((x >> 49) & 1) << 14) |
           (((x >> 50) & 1) << 13) |
           (((x >> 51) & 1) << 12) |
           (((x >> 52) & 1) << 11) |
           (((x >> 53) & 1) << 10) |
           (((x >> 54) & 1) << 9) |
           (((x >> 55) & 1) << 8) |
           (((x >> 56) & 1) << 7) |
           (((x >> 57) & 1) << 6) |
           (((x >> 58) & 1) << 5) |
           (((x >> 59) & 1) << 4) |
           (((x >> 60) & 1) << 3) |
           (((x >> 61) & 1) << 2) |
           (((x >> 62) & 1) << 1) |
           (((x >> 63) & 1) << 0);
}
3
marian adam

Generisch

C-Code. Verwenden Sie 1 Byte Eingangsdaten als Beispiel.

    unsigned char num = 0xaa;   // 1010 1010 (aa) -> 0101 0101 (55)
    int s = sizeof(num) * 8;    // get number of bits
    int i, x, y, p;
    int var = 0;                // make var data type to be equal or larger than num

    for (i = 0; i < (s / 2); i++) {
        // extract bit on the left, from MSB
        p = s - i - 1;
        x = num & (1 << p);
        x = x >> p;
        printf("x: %d\n", x);

        // extract bit on the right, from LSB
        y = num & (1 << i);
        y = y >> i;
        printf("y: %d\n", y);

        var = var | (x << i);       // apply x
        var = var | (y << p);       // apply y
    }

    printf("new: 0x%x\n", new);
2
vjangus

Wie wäre es mit dem folgenden:

    uint reverseMSBToLSB32ui(uint input)
    {
        uint output = 0x00000000;
        uint toANDVar = 0;
        int places = 0;

        for (int i = 1; i < 32; i++)
        {
            places = (32 - i);
            toANDVar = (uint)(1 << places);
            output |= (uint)(input & (toANDVar)) >> places;

        }


        return output;
    }

Klein und einfach (nur 32 Bit).

1
BlueAutumn

Ich dachte, dies ist eine der einfachsten Methoden, um das Bit umzukehren. Bitte lassen Sie mich wissen, wenn diese Logik fehlerhaft ist. Grundsätzlich überprüfen wir in dieser Logik den Wert des Bits in Position . Setzen Sie das Bit, wenn der Wert 1 in der umgekehrten Position ist.

void bit_reverse(ui32 *data)
{
  ui32 temp = 0;    
  ui32 i, bit_len;    
  {    
   for(i = 0, bit_len = 31; i <= bit_len; i++)   
   {    
    temp |= (*data & 1 << i)? (1 << bit_len-i) : 0;    
   }    
   *data = temp;    
  }    
  return;    
}    
1
Arun Nagendran

Eine andere schleifenbasierte Lösung, die schnell beendet wird, wenn die Anzahl niedrig ist (in C++ für mehrere Typen)

template<class T>
T reverse_bits(T in) {
    T bit = static_cast<T>(1) << (sizeof(T) * 8 - 1);
    T out;

    for (out = 0; bit && in; bit >>= 1, in >>= 1) {
        if (in & 1) {
            out |= bit;
        }
    }
    return out;
}

oder in C für ein vorzeichenloses int

unsigned int reverse_bits(unsigned int in) {
    unsigned int bit = 1u << (sizeof(T) * 8 - 1);
    unsigned int out;

    for (out = 0; bit && in; bit >>= 1, in >>= 1) {
        if (in & 1)
            out |= bit;
    }
    return out;
}
0
Daniel Santos
// Purpose: to reverse bits in an unsigned short integer 
// Input: an unsigned short integer whose bits are to be reversed
// Output: an unsigned short integer with the reversed bits of the input one
unsigned short ReverseBits( unsigned short a )
{
     // declare and initialize number of bits in the unsigned short integer
     const char num_bits = sizeof(a) * CHAR_BIT;

     // declare and initialize bitset representation of integer a
     bitset<num_bits> bitset_a(a);          

     // declare and initialize bitset representation of integer b (0000000000000000)
     bitset<num_bits> bitset_b(0);                  

     // declare and initialize bitset representation of mask (0000000000000001)
     bitset<num_bits> mask(1);          

     for ( char i = 0; i < num_bits; ++i )
     {
          bitset_b = (bitset_b << 1) | bitset_a & mask;
          bitset_a >>= 1;
     }

     return (unsigned short) bitset_b.to_ulong();
}

void PrintBits( unsigned short a )
{
     // declare and initialize bitset representation of a
     bitset<sizeof(a) * CHAR_BIT> bitset(a);

     // print out bits
     cout << bitset << endl;
}


// Testing the functionality of the code

int main ()
{
     unsigned short a = 17, b;

     cout << "Original: "; 
     PrintBits(a);

     b = ReverseBits( a );

     cout << "Reversed: ";
     PrintBits(b);
}

// Output:
Original: 0000000000010001
Reversed: 1000100000000000
0
MikhailJacques
unsigned char ReverseBits(unsigned char data)
{
    unsigned char k = 0, rev = 0;

    unsigned char n = data;

    while(n)

    {
        k = n & (~(n - 1));
        n &= (n - 1);
        rev |= (128 / k);
    }
    return rev;
}
0
user3615967

Ich denke, die einfachste Methode, die ich kenne, folgt. MSB ist eine Eingabe und LSB ist eine 'umgekehrte' Ausgabe:

unsigned char rev(char MSB) {
    unsigned char LSB=0;  // for output
    _FOR(i,0,8) {
        LSB= LSB << 1;
        if(MSB&1) LSB = LSB | 1;
        MSB= MSB >> 1;
    }
    return LSB;
}

//    It works by rotating bytes in opposite directions. 
//    Just repeat for each byte.
0
user7726695

Es scheint, dass viele andere Beiträge sich um die Geschwindigkeit kümmern (d. H. Am besten = am schnellsten). Was ist mit der Einfachheit? Erwägen:

char ReverseBits(char character) {
    char reversed_character = 0;
    for (int i = 0; i < 8; i++) {
        char ith_bit = (c >> i) & 1;
        reversed_character |= (ith_bit << (sizeof(char) - 1 - i));
    }
    return reversed_character;
}

und hoffe, dass der clevere Compiler für Sie optimiert wird.

Wenn Sie eine längere Liste von Bits umkehren möchten (die sizeof(char) * n-Bits enthalten), können Sie diese Funktion verwenden, um Folgendes zu erhalten:

void ReverseNumber(char* number, int bit_count_in_number) {
    int bytes_occupied = bit_count_in_number / sizeof(char);      

    // first reverse bytes
    for (int i = 0; i <= (bytes_occupied / 2); i++) {
        swap(long_number[i], long_number[n - i]);
    }

    // then reverse bits of each individual byte
    for (int i = 0; i < bytes_occupied; i++) {
         long_number[i] = ReverseBits(long_number[i]);
    }
}

Dies würde [10000000, 10101010] in [01010101, 00000001] umkehren.

0
mercury0114