Was ist der effizienteste Algorithmus, um Folgendes zu erreichen:
0010 0000 => 0000 0100
Die Konvertierung erfolgt von MSB-> LSB zu LSB-> MSB. Alle Bits müssen umgekehrt werden. Das heißt, dies ist kein Endianness-Swapping.
Was ist der effizienteste Algorithmus, um Folgendes zu erreichen:
0010 0000 => 0000 0100
Die Konvertierung erfolgt von MSB-> LSB zu LSB-> MSB. Alle Bits müssen umgekehrt werden. Das heißt, dies ist kein Endianness-Swapping.
Antworten:
HINWEIS : Alle unten aufgeführten Algorithmen sind in C, sollten jedoch in die Sprache Ihrer Wahl portierbar sein (sehen Sie mich nur nicht an, wenn sie nicht so schnell sind :)
Geringer Speicher (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));
}
Von der berühmten Bit Twiddling Hacks-Seite :
Am schnellsten (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- int
Dateien erweitern oder den Speicher gegen Geschwindigkeit austauschen (vorausgesetzt, Ihr L1-Datencache ist groß genug) und 16 Bit gleichzeitig mit einer Nachschlagetabelle mit 64 KB-Einträgen umkehren.
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 mit 32 Bit tun möchten int
, kehren Sie einfach die Bits in jedem Byte um 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);
Ich habe die beiden vielversprechendsten Lösungen verglichen, die Nachschlagetabelle und das bitweise UND (die erste). 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, 3 Versuche auf jeder Ebene durchgeführt und jeder Versuch 100 Millionen zufällige Versuche rückgängig gemacht unsigned ints
. Für die Option für die 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
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -o reverse reverse.c
mrj10@mjlap:~/code$ ./reverse
Time: 2.000593 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 1.938893 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 1.936365 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse reverse.c
mrj10@mjlap:~/code$ ./reverse
Time: 0.942709 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.991104 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.947203 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse reverse.c
mrj10@mjlap:~/code$ ./reverse
Time: 0.922639 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.892372 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.891688 seconds
Nachschlagetabelle (Option 1)
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.201127 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.196129 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.235972 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.633042 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.655880 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.633390 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.652322 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.631739 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.652431 seconds
Nachschlagetabelle (Option 2)
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.671537 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.688173 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.664662 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.049851 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.048403 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.085086 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.082223 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.053431 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.081224 seconds
Verwenden Sie die Nachschlagetabelle mit Option 1 (die Byteadressierung ist nicht überraschend langsam), wenn Sie Bedenken hinsichtlich der Leistung haben. Wenn Sie das letzte Byte Speicher aus Ihrem System herausholen müssen (und wenn Sie sich für die Leistung der Bitumkehr interessieren), sind die optimierten Versionen des bitweisen UND-Ansatzes auch nicht allzu schäbig.
Ja, ich weiß, dass der Benchmark-Code ein vollständiger Hack ist. Vorschläge zur Verbesserung sind mehr als willkommen. Dinge, die ich weiß:
ld
ist ein verrückter Fehler bei der Neudefinition von Symbolen aufgetreten ), daher glaube ich nicht, dass der generierte Code für meine Mikroarchitektur optimiert ist.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
EDIT: Ich habe es auch versucht uint64_t
Typen auf meinem Computer zu verwenden, um festzustellen, ob es eine Leistungssteigerung gab. Die Leistung war etwa 10% schneller als die von 32-Bit und war nahezu identisch, unabhängig davon, ob Sie nur 64-Bit-Typen zum gleichzeitigen Umkehren von Bits auf zwei 32-Bit- int
Typen verwendeten oder ob Sie tatsächlich Bits in halb so vielen 64-Bit-Typen umkehrten. Bitwerte. Der Assembler-Code wird unten gezeigt (für den ersteren Fall Umkehren von Bits für zwei 32-Bit- int
Typen gleichzeitig):
.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
Dieser Thread hat meine Aufmerksamkeit erregt, da er sich mit einem einfachen Problem befasst, das selbst für eine moderne CPU viel Arbeit (CPU-Zyklen) erfordert. Und eines Tages stand ich auch mit dem gleichen ¤ #% "#" Problem da. Ich musste Millionen von Bytes umdrehen. Ich weiß jedoch, dass alle meine Zielsysteme auf modernem Intel basieren. Beginnen wir also mit der Optimierung auf das Äußerste !!!
Also habe ich Matt Js Lookup-Code als Basis verwendet. Das System, auf dem ich ein Benchmarking durchführe, ist ein i7 haswell 4700eq.
Matt Js Lookup-Bitflipping 400 000 000 Bytes: Ungefähr 0,272 Sekunden.
Ich ging dann voran und versuchte zu sehen, ob Intels ISPC-Compiler die Arithmetik in umgekehrter Reihenfolge vektorisieren konnte. C.
Ich werde Sie hier nicht mit meinen Erkenntnissen langweilen, da ich viel versucht habe, dem Compiler bei der Suche nach Dingen zu helfen. Trotzdem hatte ich eine Leistung von ungefähr 0,15 Sekunden, um 400 000 000 Bytes zu bitflippen. Es ist eine großartige Reduzierung, aber für meine Anwendung ist das immer noch viel zu langsam.
Die Leute ließen mich den schnellsten Intel-basierten Bitflipper der Welt vorstellen. 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 benötigt 32 Bytes und maskiert dann die Knabbereien. Das hohe Halbbyte wird um 4 nach rechts verschoben. Dann verwende ich vpshufb und ymm4 / ymm3 als Nachschlagetabellen. Ich könnte eine einzelne Nachschlagetabelle verwenden, aber dann müsste ich nach links wechseln, bevor ich die Knabbereien wieder zusammenfügen kann.
Es gibt noch schnellere Möglichkeiten, die Bits umzudrehen. Aber ich bin an Single Thread und CPU gebunden, also war dies die schnellste, die ich erreichen konnte. Kannst du eine schnellere Version machen?
Bitte machen Sie keine Kommentare zur Verwendung der Intel C / C ++ Compiler Intrinsic Equivalent-Befehle ...
pshub
, denn schließlich wird auch der beste Popcount damit gemacht! Ich hätte es hier geschrieben, wenn nicht für dich. Ein großes Lob.
popcnt
, tzcnt
und pext
alle an Port 1. Also kostet jeder pext
oder jeder tzcnt
Sie einen popcnt
Durchsatz. Wenn Ihre Daten im L1D-Cache heiß sind, können Sie ein Array auf Intel-CPUs am schnellsten mit AVX2 pshufb zählen. (Ryzen hat einen popcnt
Durchsatz von 4 pro Takt , das ist wahrscheinlich optimal, aber die Bulldozer-Familie hat einen Durchsatz von 4 pro Takt popcnt r64,r64
... agner.org/optimize ).
Dies ist eine weitere Lösung für Leute, die Rekursion lieben.
Die Idee ist einfach. Teilen Sie die Eingabe durch die Hälfte und tauschen Sie die beiden Hälften aus. Fahren Sie fort, bis das einzelne Bit erreicht ist.
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, damit es für Eingaben bis zu einer Größe von (vorzeichenlosen Int) * 8 Bit verwendet werden kann.
Die rekursive Funktion akzeptiert 2 Parameter - den Wert, dessen Bits umgekehrt werden müssen, 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
numBits
ist int, wenn Sie 3 durch 2 für den Funktionsparameter teilen, wird es auf 1 abgerundet?
Nun, dies wird sicherlich keine Antwort wie die von Matt J sein, aber hoffentlich wird es immer noch 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 bei Matts bestem Algorithmus, außer dass es diesen kleinen Befehl namens BSWAP gibt, der die Bytes (nicht die Bits) einer 64-Bit-Zahl vertauscht. So wird aus b7, b6, b5, b4, b3, b2, b1, b0 b0, b1, b2, b3, b4, b5, b6, b7. Da wir mit einer 32-Bit-Nummer arbeiten, müssen wir unsere bytegetauschte Nummer um 32 Bit nach unten verschieben. Dies lässt uns nur die Aufgabe, die 8 Bits jedes Bytes auszutauschen, was erledigt ist und voila! Wir sind fertig.
Timing: Auf meinem Computer lief Matts Algorithmus in ~ 0,52 Sekunden pro Versuch. Meins lief in ungefähr 0,42 Sekunden pro Versuch. 20% schneller ist nicht schlecht, denke ich.
Wenn Sie sich Sorgen über die Verfügbarkeit der Anweisung BSWAP Wikipedia machen listet den Befehl BSWAP als mit 80846 hinzugefügt auf, der 1989 herauskam. Es sollte beachtet werden, dass Wikipedia auch angibt, dass dieser Befehl nur mit 32-Bit-Registern funktioniert, was eindeutig nicht der Fall ist Fall auf meinem Computer funktioniert es sehr viel nur auf 64-Bit-Registern.
Diese Methode funktioniert für jeden integralen Datentyp gleich gut, sodass die Methode trivial verallgemeinert werden kann, indem die gewünschte Anzahl von Bytes übergeben wird:
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;
}
was dann wie folgt aufgerufen werden kann:
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, den zusätzlichen Parameter zu optimieren (vorausgesetzt, der Compiler integriert die Funktion), und für den sizeof(size_t)
Fall würde die Rechtsverschiebung vollständig entfernt. Beachten Sie, dass GCC zumindest nicht in der Lage ist, BSWAP und Rechtsverschiebung zu entfernen, wenn es bestanden wird sizeof(char)
.
Die Antwort von Anders Cedronius bietet eine großartige Lösung für Benutzer mit einer x86-CPU mit AVX2-Unterstützung. 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 auf verschiedenen ARM-Prozessoren nützlichen Shift-Plus-Logik-Idioms maximiert wird. Darüber hinaus wird die On-the-Fly-Maskengenerierung verwendet, was für RISC-Prozessoren von Vorteil sein kann, die ansonsten mehrere Anweisungen zum Laden jedes 32-Bit-Maskenwerts benötigen. Compiler für x86-Plattformen sollten eine konstante Weitergabe 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 Möglichkeiten 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, wird in diesem Dokument auf der Hacker's Delight-Website gezeigt.
/* 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;
}
Mit dem Intel Compiler C / C ++ - Compiler 13.1.3.198 werden beide oben genannten Funktionen automatisch vektorisiert XMM
Registerregister . Sie können auch ohne großen Aufwand manuell vektorisiert werden.
Auf meinem IvyBridge Xeon E3 1270v2 wurden unter Verwendung des automatisch vektorisierten Codes 100 Millionen uint32_t
Wörter in 0,070 Sekunden mit brev_classic()
und 0,068 Sekunden mit bitumgekehrt brev_knuth()
. Ich habe darauf geachtet, dass mein Benchmark nicht durch die Bandbreite des Systemspeichers begrenzt ist.
brev_knuth()
? Die Zuschreibung im PDF von Hacker's Delight scheint darauf hinzudeuten, dass diese Zahlen direkt von Knuth selbst stammen. Ich kann nicht behaupten, Knuths Beschreibung der zugrunde liegenden Entwurfsprinzipien in TAOCP ausreichend verstanden zu haben, um zu erklären, wie die Konstanten abgeleitet wurden oder wie man die abgeleiteten Konstanten und Verschiebungsfaktoren für beliebige Wortgrößen vorgehen würde.
Angenommen, Sie haben ein Array von Bits, wie wäre es damit: 1. Schieben Sie die Bits ausgehend von MSB nacheinander in einen Stapel. 2. Pop-Bits von diesem Stapel in ein anderes Array (oder dasselbe Array, wenn Sie Platz sparen möchten), platzieren Sie das erste Popped-Bit in MSB und fahren Sie von dort aus mit weniger signifikanten Bits fort.
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();
}
Für einen Menschen ist das kein Job! ... aber perfekt für eine Maschine
Dies ist 2015, 6 Jahre nachdem diese Frage zum ersten Mal gestellt wurde. Compiler sind seitdem unsere Meister geworden, und unsere Aufgabe als Mensch ist es nur, ihnen zu helfen. Was ist der beste Weg, um der Maschine unsere Absichten zu geben?
Bit-Umkehrung ist so häufig, dass Sie sich fragen müssen, warum die ständig wachsende ISA des x86 keine Anweisung enthält, dies auf einmal zu tun.
Der Grund: Wenn Sie dem Compiler Ihre wahre, präzise Absicht geben, sollte die Bitumkehr nur ~ 20 CPU-Zyklen dauern . Lassen Sie mich Ihnen zeigen, wie Sie reverse () herstellen 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;
}
Das Kompilieren dieses Beispielprogramms mit der Clang-Version> = 3.6, -O3, -march = native (getestet mit Haswell) liefert Code in Grafikqualität unter Verwendung der neuen AVX2-Anweisungen mit einer Laufzeit von 11 Sekunden , die ~ 1 Milliarde reverse () s verarbeitet. Das sind ~ 10 ns pro Umkehrung (), wobei ein CPU-Zyklus von 0,5 ns bei 2 GHz die süßen 20 CPU-Zyklen erreicht.
Vorsichtsmaßnahme: Dieser Beispielcode sollte einige Jahre lang als anständiger Maßstab dienen, aber er wird irgendwann sein Alter zeigen, sobald die Compiler klug genug sind, main () zu optimieren, um nur das Endergebnis auszudrucken, anstatt wirklich etwas zu berechnen. Aber im Moment funktioniert es, um reverse () zu präsentieren.
Bit-reversal is so common...
Das weiß ich nicht. Ich arbeite mit Code, der praktisch jeden Tag mit Daten auf Bitebene umgeht, und ich kann mich nicht erinnern, jemals dieses spezielle Bedürfnis gehabt zu haben. In welchen Szenarien brauchen Sie es? - Nicht, dass es kein interessantes Problem wäre, es selbst zu lösen.
Natürlich ist die offensichtliche Quelle für Bit-Twiddling-Hacks hier: http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
Ich weiß, es ist nicht C, 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, sodass Sie auch Flags speichern können
rcl
CF verschieben var1
, anstatt nur shl
Flags zu lesen. (Oder adc dx,dx
). Selbst mit diesem Fix ist dies lächerlich langsam, wenn man die langsame loop
Anweisung verwendet und var1
im Gedächtnis bleibt ! Eigentlich denke ich, dass dies die Ausgabe in AX erzeugen soll, aber es speichert / stellt den alten Wert von AX über dem Ergebnis wieder her.
Nun, dies ist im Grunde dasselbe wie das erste "reverse ()", aber es ist 64 Bit und benötigt nur eine sofortige Maske, um aus dem Befehlsstrom geladen zu werden. GCC erstellt Code ohne Sprünge, daher sollte dies ziemlich schnell gehen.
#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;
}
Ich war gespannt, wie schnell die offensichtliche Rohrotation sein würde. Auf meinem Computer (i7 @ 2600) lag der Durchschnitt für 1.500.150.000 Iterationen 27.28 ns
(über einen zufälligen Satz von 131.071 64-Bit-Ganzzahlen).
Vorteile: Der Speicherbedarf ist gering und der Code einfach. Ich würde sagen, es ist auch nicht so groß. Die erforderliche Zeit ist für jede Eingabe vorhersehbar und konstant (128 arithmetische SHIFT-Operationen + 64 logische UND-Operationen + 64 logische ODER-Operationen).
Ich habe mit der besten Zeit verglichen, die @Matt J erhalten hat - der die akzeptierte Antwort hat. Wenn ich seine Antwort richtig lese, ist das Beste, was er hat, 0.631739
Sekunden für 1,000,000
Iterationen, was zu einem Durchschnitt von 631 ns
pro Umdrehung führt.
Das Code-Snippet, das ich verwendet habe, ist das 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);
}
Möglicherweise möchten Sie die Standardvorlagenbibliothek verwenden. Es ist möglicherweise langsamer als der oben genannte Code. Es scheint mir jedoch klarer und leichter zu verstehen.
#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;
}
Generisch
C-Code. Verwenden Sie als Beispiel die 1-Byte-Eingabedaten num.
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);
Wie wäre es mit folgendem:
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 (allerdings nur 32 Bit).
Ich dachte, dies ist einer der einfachsten Wege, 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 in umgekehrter Position 1 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;
}
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;
}
k
ist immer eine Potenz von 2, aber Compiler werden das wahrscheinlich nicht beweisen und es in Bit-Scan / Shift umwandeln.
Ich denke, die einfachste Methode, die ich kenne, folgt. MSB
ist Eingabe und LSB
ist '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.
// 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
Eine weitere 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;
}
Es scheint, dass viele andere Beiträge über die Geschwindigkeit besorgt sind (dh am besten = am schnellsten). Was ist mit 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.
Wenn Sie eine längere Liste von Bits (die sizeof(char) * n
Bits enthalten ) umkehren möchten , 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.
ith_bit = (c >> i) & 1
. Auch speichert SUB durch Verschieben reversed_char
statt das Stück verschoben wird , es sei denn , Sie hoffen , es auf x86 kompilieren wird sub something
/ bts reg,reg
das n - te Bit im Zielregister zu setzen.
Bitumkehr im Pseudocode
Quelle -> umzukehrendes Byte b00101100 Ziel -> umgekehrt, muss ebenfalls vom Typ ohne Vorzeichen sein, damit das Vorzeichenbit nicht nach unten übertragen wird
Kopieren in Temp, damit das Original nicht betroffen ist. Es muss auch vom Typ ohne Vorzeichen sein, damit das Vorzeichenbit nicht automatisch verschoben wird
bytecopy = b0010110
LOOP8: // Diesen 8-maligen Test durchführen, wenn die Bytekopie <0 ist (negativ)
set bit8 (msb) of reversed = reversed | b10000000
else do not set bit8
shift bytecopy left 1 place
bytecopy = bytecopy << 1 = b0101100 result
shift result right 1 place
reversed = reversed >> 1 = b00000000
8 times no then up^ LOOP8
8 times yes then done.
Meine einfache Lösung
BitReverse(IN)
OUT = 0x00;
R = 1; // Right mask ...0000.0001
L = 0; // Left mask 1000.0000...
L = ~0;
L = ~(i >> 1);
int size = sizeof(IN) * 4; // bit size
while(size--){
if(IN & L) OUT = OUT | R; // start from MSB 1000.xxxx
if(IN & R) OUT = OUT | L; // start from LSB xxxx.0001
L = L >> 1;
R = R << 1;
}
return OUT;
i
? Was ist diese magische Konstante * 4
? Ist es CHAR_BIT / 2
?
Dies ist für 32 Bit, wir müssen die Größe ändern, wenn wir 8 Bit berücksichtigen.
void bitReverse(int num)
{
int num_reverse = 0;
int size = (sizeof(int)*8) -1;
int i=0,j=0;
for(i=0,j=size;i<=size,j>=0;i++,j--)
{
if((num >> i)&1)
{
num_reverse = (num_reverse | (1<<j));
}
}
printf("\n rev num = %d\n",num_reverse);
}
Lesen der Eingabe-Ganzzahl "num" in der Reihenfolge LSB-> MSB und Speichern in num_reverse in der Reihenfolge MSB-> LSB.
int bit_reverse(int w, int bits)
{
int r = 0;
for (int i = 0; i < bits; i++)
{
int bit = (w & (1 << i)) >> i;
r |= bit << (bits - i - 1);
}
return r;
}