Aşağıdakileri başarmak için en etkili algoritma nedir:
0010 0000 => 0000 0100
Dönüşüm MSB-> LSB'den LSB-> MSB'ye yapılır. Tüm bitler ters çevrilmelidir; yani bu endianness takası değildir .
Aşağıdakileri başarmak için en etkili algoritma nedir:
0010 0000 => 0000 0100
Dönüşüm MSB-> LSB'den LSB-> MSB'ye yapılır. Tüm bitler ters çevrilmelidir; yani bu endianness takası değildir .
Yanıtlar:
NOT : Aşağıdaki tüm algoritmalar C cinsindendir, ancak seçtiğiniz dil için taşınabilir olmalıdır (bu kadar hızlı olmadıklarında bana bakmayın :)
Düşük Bellek (32 bit int
, 32 bit makine) ( buradan ):
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));
}
Ünlü Bit Twiddling Hacks sayfasından :
En hızlı (arama tablosu) :
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]];
Bu fikri 64 bit int
s'ye genişletebilir veya hız için bellek kapatabilirsiniz (L1 Veri Önbelleğinizin yeterince büyük olduğu varsayılarak) ve 64K girişli arama tablosuyla bir seferde 16 bit tersine çevirebilirsiniz.
Basit
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
Daha hızlı (32 bit işlemci)
unsigned char b = x;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
Daha hızlı (64 bit işlemci)
unsigned char b; // reverse this (8-bit) byte
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
Bunu 32 bit üzerinde yapmak istiyorsanız int
, her bayttaki bitleri tersine çevirin ve baytların sırasını tersine çevirin. Yani:
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);
En umut verici iki çözümü, arama tablosunu ve bitsel-AND'i (ilk çözüm) karşılaştırdım. Test makinesi, 4GB DDR2-800'lü bir dizüstü bilgisayar ve bir Core 2 Duo T7500 @ 2.4GHz, 4MB L2 Önbellek; YMMV. 64 bit Linux'ta gcc 4.3.2 kullandım . OpenMP (ve GCC bağlamaları) yüksek çözünürlüklü zamanlayıcılar için kullanıldı.
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;
}
Her iki yaklaşımı da birkaç farklı optimizasyonda denedim, her seviyede 3 deneme yaptım ve her deneme 100 milyon rastgele tersine döndü unsigned ints
. Arama tablosu seçeneği için, bitsel kesmek sayfasında verilen her iki şemayı (seçenek 1 ve 2) denedim. Sonuçlar aşağıda gösterilmiştir.
Bitsel VE
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
Arama Tablosu (seçenek 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
Arama Tablosu (seçenek 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
Performans konusunda endişeleriniz varsa seçenek tablosuyla (bayt adresleme şaşırtıcı derecede yavaştır) arama tablosunu kullanın . Her bir son bayt belleği sisteminizden çıkarmanız gerekiyorsa (ve bit tersine çevirme performansını önemsiyorsanız), bitsel-AND yaklaşımının optimize edilmiş sürümleri de çok düşkün değildir.
Evet, karşılaştırma kodu tam bir kesmek olduğunu biliyorum. Nasıl geliştirileceğine dair öneriler memnuniyetle karşılanmaktadır. Bildiğim şeyler:
ld
bazı çılgın sembol yeniden tanımlama hatası ile patladı), bu yüzden oluşturulan kodun mikro mimarim için ayarlandığına inanmıyorum.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: Ayrıca uint64_t
herhangi bir performans artışı olup olmadığını görmek için makinemde türleri kullanmayı denedim . Performans 32 bit'ten yaklaşık% 10 daha hızlıydı int
ve bir seferde iki 32 bitlik tipte bitleri tersine çevirmek için sadece 64 bitlik tipler kullanıyor olsanız da , ya da gerçekten 64- bit değerleri. Montaj kodu aşağıda gösterilmiştir (önceki durum için int
, bir seferde iki 32 bit tip için ters bitler ):
.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
Bu iş parçacığı dikkatimi çekti, çünkü modern bir CPU için bile çok fazla iş (CPU döngüleri) gerektiren basit bir sorunla ilgileniyor. Ve bir gün orada aynı ¤ #% "#" sorunuyla durdum. Milyonlarca baytı çevirmek zorunda kaldım. Ancak tüm hedef sistemlerimin modern Intel tabanlı olduğunu biliyorum, bu yüzden aşırıya optimize etmeye başlayalım !!!
Baz olarak Matt J'nin arama kodunu kullandım. Kıyasladığım sistem bir i7 haswell 4700eq.
Matt J'nin arama biti 400 000 000 bayt çevirme: Yaklaşık 0,272 saniye.
Daha sonra devam ettim ve Intel'in ISPC derleyicisinin aritmetiği tersine çevirip çeviremediğini görmeye çalıştım. C.
Derleyicinin bir şeyler bulmasına yardımcı olmak için çok çalıştığımdan, sizi burada bulgularımla sıkmayacağım, her neyse 400 000 000 bayt bitflip için yaklaşık 0.15 saniye performansla sonuçlandım. Bu büyük bir azalma ama benim uygulama için bu hala çok yavaş.
İnsanlar dünyadaki en hızlı Intel tabanlı bitflipper'ı sunmama izin verdi. Saat:
Bitflip 400000000 bayt: 0.050082 saniye !!!!!
// 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;
}
Printf'ler hata ayıklama içindir.
İşte beygir:
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
Kod 32 bayt alır, ardından kemikleri maskeler. Yüksek kemirmek 4'e doğru kaydırılır. Sonra arama tabloları olarak vpshufb ve ymm4 / ymm3 kullanıyorum. Tek bir arama tablosu kullanabilirsiniz ama sonra tekrar nibbles ORing önce sola kaydırmak gerekir.
Uçları çevirmenin daha hızlı yolları var. Ama tek iş parçacığına ve CPU'ya bağlıyım, bu yüzden başarabildiğim en hızlı şeydi. Daha hızlı bir sürüm yapabilir misiniz?
Lütfen Intel C / C ++ Compiler Intrinsic Eşdeğer komutlarını kullanma hakkında yorum yapmayın ...
pshub
, çünkü sonuçta en iyi popcount da bununla yapılıyor! Senin için olmasaydı buraya yazardım. Kudos.
popcnt
, tzcnt
ve pext
tüm port 1. her Yani pext
ya tzcnt
maliyetlerin size popcnt
throughput. Verileriniz L1D önbelleğinde sıcaksa, Intel CPU'larda bir dizi oluşturmanın en hızlı yolu AVX2 pshufb'dur. (Ryzen'de saat başına 4 popcnt
iş hacmi vardır, bu muhtemelen en uygunudur, ancak Bulldozer ailesinde 4 saatte birpopcnt r64,r64
iş çıkışı vardır ... agner.org/optimize ).
Bu, özyinelemeyi seven insanlar için başka bir çözümdür.
Fikir basit. Girdiyi yarıya bölün ve iki yarıyı değiştirin, tek bite ulaşıncaya kadar devam edin.
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
İşte bunu çözmek için özyinelemeli bir işlev. (Not İmzasız ints kullandım, bu yüzden sizeof (unsigned int) * 8 bit'e kadar girişler için çalışabilir.
Özyinelemeli işlev 2 parametre alır - Bitleri ters çevrilmesi gereken değer ve değerdeki bit sayısı.
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);
}
Bu çıktı:
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
İnt gibi , işlev parametresi için 3'ü 2'ye böldüğünüzde 1'e yuvarlanır?
Bu kesinlikle Matt J'inki gibi bir cevap olmayacak, ancak umarım yine de faydalı olacaktır.
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;
}
Bu, Matt'in en iyi algoritmasıyla tamamen aynı fikirdir, ancak BSWAP adı verilen ve 64 bitlik bir sayının baytlarını (bitlerini değil) değiştiren bu küçük komut vardır. Böylece b7, b6, b5, b4, b3, b2, b1, b0, b0, b1, b2, b3, b4, b5, b6, b7 olur. 32 bitlik bir sayı ile çalıştığımız için bayt-takas edilen sayımızı 32 bit aşağı kaydırmamız gerekir. Bu bizi, yapılan her baytın 8 bitini ve işini değiştirmekle görevlendirir! yapılmıştı.
Zamanlama: Makinemde, Matt'in algoritması deneme başına ~ 0.52 saniye içinde çalıştı. Mayın deneme başına yaklaşık 0,42 saniye içinde koştu. % 20 daha hızlı kötü değil bence.
Talimatın kullanılabilirliğinden endişe ediyorsanız BSWAP Wikipedia , 1989'da çıkan 80846 ile eklenen BSWAP talimatını listelemektedir. Wikipedia'nın da bu talimatın sadece 32 bit kayıtlarda çalıştığını ve benim durumumda, sadece 64 bit kayıtlarda çok işe yarıyor.
Bu yöntem, herhangi bir tümleşik veri tipi için eşit derecede iyi çalışacaktır, böylece yöntem, istenen bayt sayısını geçirerek önemsiz bir şekilde genelleştirilebilir:
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;
}
daha sonra şöyle adlandırılabilir:
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
Derleyici, ekstra parametreyi uzağa optimize edebilmelidir (derleyicinin işlevi satır içine aldığı varsayılarak) ve sizeof(size_t)
durumda sağ kaydırma tamamen kaldırılacaktır. GCC'nin en azından BSWAP'ı kaldıramaz ve geçilirse sağa kaydıramaz sizeof(char)
.
Anders Cedronius'un cevabı , AVX2 destekli x86 CPU'ya sahip insanlar için harika bir çözüm sunuyor. AVX desteği olmayan x86 platformları veya x86 olmayan platformlar için, aşağıdaki uygulamalardan herhangi birinin iyi çalışması gerekir.
İlk kod, klasik ARM bölümleme yönteminin bir çeşididir ve çeşitli ARM işlemcilerinde yararlı olan shift-plus-logic deyimin kullanımını en üst düzeye çıkarmak için kodlanmıştır. Buna ek olarak, her bir 32 bit maske değerini yüklemek için birden fazla talimat gerektiren RISC işlemcileri için yararlı olabilecek anında maske oluşturma özelliğini kullanır. X86 platformları için derleyiciler çalışma zamanı yerine derleme zamanında tüm maskeleri hesaplamak için sürekli yayılım kullanmalıdır.
/* 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;
}
"Bilgisayar Programlama Sanatı" nın 4A cildinde, D. Knuth, klasik ikili bölümleme algoritmalarından bir şekilde şaşırtıcı bir şekilde daha az işlem gerektiren bitleri tersine çevirmenin akıllı yollarını göstermektedir. TAOCP'de bulamadığım 32 bit işlenenler için böyle bir algoritma, bu belgede Hacker'ın Delight web sitesinde gösterilmektedir.
/* 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;
}
Intel derleyici C / C ++ derleyici 13.1.3.198 kullanarak, yukarıdaki işlevlerin her ikisi de güzel hedefleme XMM
kayıtlarını otomatik olarak vektörleştirir . Ayrıca çok fazla çaba harcamadan manuel olarak vektörleştirilebilirler.
IvyBridge Xeon E3 1270v2'de, otomatik vektör kodunu kullanarak, 100 milyon uint32_t
kelime kullanarak 0.070 saniyede brev_classic()
ve 0.068 saniyede bit ters çevrildi brev_knuth()
. Karşılaştırmamın sistem belleği bant genişliği ile sınırlı olmadığından emin olmaya özen gösterdim.
brev_knuth()
? Hacker'ın Lokumu'ndaki PDF'deki atıf, bu sayıların doğrudan Knuth'un kendisinden geldiğini gösteriyor. Knuth'un TAOCP'deki temel tasarım ilkelerini tanımlamasını sabitlerin nasıl türetildiğini veya rastgele kelime boyutları için türetici sabitler ve kayma faktörleri hakkında nasıl ilerleyeceğini açıkladığını iddia edemem.
Bir dizi bitiniz olduğu varsayılarak, buna ne dersiniz: 1. MSB'den başlayarak, bitleri birer birer yığına itin. 2. Bu yığındaki bitleri başka bir diziye (veya yerden tasarruf etmek istiyorsanız aynı diziye) açın, ilk atlanan biti MSB'ye yerleştirin ve oradan daha az önemli bitlere geçin.
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();
}
Bu bir insan için iş değil! ... ama bir makine için mükemmel
Bu 2015, bu sorunun ilk sorulduğu 6 yıl. Derleyiciler o zamandan beri efendimiz haline geldi ve insan olarak bizim işimiz sadece onlara yardım etmek. Peki, niyetimizi makineye vermenin en iyi yolu nedir?
Bit tersine çevirme o kadar yaygındır ki, x86'nın sürekli büyüyen ISA'sının neden tek seferlik bir talimat içermediğini merak etmelisiniz.
Nedeni: Eğer derleyiciye gerçek öz niyetini verirseniz, bit tersine çevirme sadece ~ 20 CPU döngüsü sürmelidir . Size tersine () nasıl yapılacağını göstereyim ve kullanayım:
#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;
}
Bu örnek programı Clang sürümü> = 3.6, -O3, -march = native (Haswell ile test edilmiştir) ile derlemek, yeni AVX2 talimatlarını kullanarak resim kalitesinde kod verir, çalışma süresi 11 saniye ~ 1 milyar ters (s) işlenir. 2 GHz varsayımıyla .5 ns CPU döngüsü, bizi tatlı 20 CPU döngüsüne soktuğunu varsayarsak.
Dikkat: Bu örnek kod birkaç yıl boyunca iyi bir kıyaslama ölçütü olarak kullanılmalıdır, ancak derleyiciler, gerçekten herhangi bir şey hesaplamak yerine nihai sonucu yazdırmak için main () 'yi optimize edecek kadar akıllı olduktan sonra yaşını göstermeye başlayacaktır. Ama şimdilik ters () vitrine çıkıyor.
Bit-reversal is so common...
Bunu bilmiyorum. Hemen hemen her gün bit düzeyinde veri ile ilgilenen kod ile çalışır ve hiç bu özel ihtiyacı vardı hatırlayamıyorum. Hangi senaryolara ihtiyacınız var? - Kendi başına çözmenin ilginç bir sorun olmadığı değil.
Tabii ki bit-twling kesmek açık kaynak: http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
C değil asm biliyorum:
var1 dw 0f0f0
clc
push ax
push cx
mov cx 16
loop1:
shl var1
shr ax
loop loop1
pop ax
pop cx
Bu, taşıma bitiyle çalışır, böylece bayrakları da kaydedebilirsiniz
rcl
CF'yi var1
sadece shl
bayrakları okumamak yerine kaydırmak istiyorsunuz . (Veya adc dx,dx
). Bu düzeltmeyle bile, bu yavaş loop
talimatı kullanarak ve var1
hafızada tutarak gülünç yavaştır ! Aslında bunun AX'de çıktı üretmesi gerektiğini düşünüyorum, ancak AX'in eski değerini sonucun üzerine kaydeder / geri yükler.
Bu temelde ilk "reverse ()" ile aynıdır, ancak 64 bittir ve komut akışından yüklenmesi için sadece bir acil maskeye ihtiyaç vardır. GCC, atlama olmadan kod oluşturur, bu yüzden bu oldukça hızlı olmalıdır.
#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;
}
Açıkça ham dönüşün ne kadar hızlı olacağını merak ediyordum. Makinemde (i7 @ 2600), 1.500.150.000 yineleme için ortalama 27.28 ns
(rastgele bir 131.071 64 bit tamsayı kümesi) idi.
Avantajları: Gerekli bellek miktarı azdır ve kod basittir. Ben de o kadar büyük olmadığını söyleyebilirim. Gerekli süre, herhangi bir girdi için öngörülebilir ve sabittir (128 aritmetik SHIFT işlemi + 64 mantıksal AND işlemleri + 64 mantıksal VEYA işlemleri).
Kabul edilen cevabı veren @Matt J tarafından elde edilen en iyi zamanla karşılaştırdım. Cevabını doğru bir şekilde okursam, elde ettiği en iyi şey, iterasyonlar 0.631739
için saniyelerdi 1,000,000
, bu da 631 ns
rotasyon başına ortalama bir değere yol açtı .
Kullandığım kod snippet'i aşağıdadır:
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);
}
Standart şablon kütüphanesini kullanmak isteyebilirsiniz. Yukarıda belirtilen koddan daha yavaş olabilir. Ancak bana anlaşılır ve anlaşılması daha kolay geliyor.
#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;
}
genel
C kodu. Örnek olarak 1 bayt giriş verisi 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);
Aşağıdakiler nasıl olur:
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;
}
Küçük ve kolay (gerçi, sadece 32 bit).
Bunun biraz tersine çevirmenin en basit yollarından biri olduğunu düşündüm. lütfen bu mantıkta herhangi bir kusur olup olmadığını bana bildirin. temel olarak bu mantıkta, bitin pozisyondaki değerini kontrol ederiz. değer ters konumda 1 ise biti ayarlayın.
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
her zaman 2 gücüdür, ancak derleyiciler muhtemelen bunu kanıtlamaz ve bit tarama / kaydırmaya dönüştürmez.
Bence bildiğim en basit yöntem şu. MSB
girdi ve LSB
'ters' çıktıdır:
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
Sayı düşük olduğunda hızla çıkan başka bir döngü tabanlı çözüm (birden çok tür için C ++ ile)
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;
}
veya imzalanmamış bir int için C
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;
}
Görünüşe göre diğer pek çok gönderi hız konusunda endişe duyuyor (en iyi = en hızlı). Basitliğe ne olacak? Düşünmek:
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;
}
ve akıllı derleyicinin sizin için optimize edeceğini umuyoruz.
Daha uzun bir bit listesini (bit içeren sizeof(char) * n
) tersine çevirmek istiyorsanız, aşağıdakileri elde etmek için bu işlevi kullanabilirsiniz:
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]);
}
}
Bu [10000000, 10101010] 'u [01010101, 00000001]' e çevirir.
ith_bit = (c >> i) & 1
. Ayrıca , hedef kaydındaki n'inci biti ayarlamak için reversed_char
x86 üzerinde sub something
/ derleneceğini ummadıkça, biti değiştirmek yerine kaydırarak bir SUB kaydedin bts reg,reg
.
Sahte kodda bit tersine çevirme
kaynak -> ters çevrilecek bayt b00101100 hedef -> ters çevrilmiş, ayrıca işaretsiz tipte olmalıdır, bu nedenle işaret biti propogasyona tabi tutulmaz
orijinalin etkilenmemesi için geçici olarak kopyalayın, ayrıca işaretsiz tipte olması gerekir, böylece işaret biti otomatik olarak kaydırılmaz
bytecopy = b0010110
LOOP8: // baytkopi <0 (negatif) ise bunu 8 kez test edin
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.
Benim basit çözümüm
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
? Ayrıca, bu büyü sabiti * 4
nedir? Öyle mi CHAR_BIT / 2
?
Bu 32 bit içindir, 8 biti düşünürsek boyutu değiştirmeliyiz.
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);
}
LSB-> MSB sıralamasında "num" giriş tamsayısının okunması ve MSB-> LSB sıralamasında num_reverse kaydedilmesi.
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;
}