다음을 달성하는 가장 효율적인 알고리즘은 무엇입니까?
0010 0000 => 0000 0100
MSB-> LSB에서 LSB-> MSB로 변환됩니다. 모든 비트는 반대로해야합니다. 즉, 이것은 엔디안 스왑 이 아닙니다 .
다음을 달성하는 가장 효율적인 알고리즘은 무엇입니까?
0010 0000 => 0000 0100
MSB-> LSB에서 LSB-> MSB로 변환됩니다. 모든 비트는 반대로해야합니다. 즉, 이것은 엔디안 스왑 이 아닙니다 .
답변:
노트 : 아래의 모든 알고리즘은 C 언어이지만 원하는 언어로 이식 가능해야합니다 (빠르지 않을 때 나를 보지 마십시오).
메모리 부족 (32 비트 int
, 32 비트 시스템) ( 여기에서 ) :
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));
}
유명한 Bit Twiddling Hacks 페이지에서 :
가장 빠른 (조회 테이블) :
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]];
이 아이디어를 64 비트로 확장 int
하거나 속도를 위해 메모리를 교환 (L1 데이터 캐시가 충분히 크다고 가정)하고 64K 항목 조회 테이블을 사용하여 한 번에 16 비트를 되돌릴 수 있습니다.
단순한
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
더 빠른 (32 비트 프로세서)
unsigned char b = x;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
더 빠른 (64 비트 프로세서)
unsigned char b; // reverse this (8-bit) byte
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
32 비트 에서이 작업을 수행 int
하려면 각 바이트의 비트를 뒤집고 바이트 순서를 반대로하십시오. 그건:
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);
가장 유망한 두 가지 솔루션 인 조회 테이블과 비트 단위 AND (첫 번째)를 벤치마킹했습니다. 테스트 머신은 4GB DDR2-800이 장착 된 랩탑 및 2.4GHz, Core 2 Duo T7500, 4MB L2 캐시입니다. YMMV. 64 비트 Linux에서 gcc 4.3.2를 사용했습니다 . OpenMP (및 GCC 바인딩)는 고해상도 타이머에 사용되었습니다.
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;
}
나는 여러 가지 최적화에서 두 가지 접근 방식을 시도하고 각 수준에서 3 번의 시험을 실행했으며 각 시험은 1 억 개의 무작위를 거꾸로했습니다 unsigned ints
. 조회 테이블 옵션의 경우 비트 해킹 페이지에 제공된 두 가지 구성표 (옵션 1 및 2)를 모두 시도했습니다. 결과는 아래와 같습니다.
비트 AND
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
조회 테이블 (옵션 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
조회 테이블 (옵션 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
옵션 1과 함께 찾아보기 테이블을 사용하십시오.성능이 염려 될 경우 (바이트 주소 지정이 예상보다 느림) . 시스템에서 모든 마지막 바이트의 메모리를 짜야하는 경우 (그리고 비트 반전 성능에 관심이있는 경우) 비트 AND 접근 방식의 최적화 된 버전도 너무 초라하지 않습니다.
예, 벤치 마크 코드가 완전한 해킹이라는 것을 알고 있습니다. 그것을 개선하는 방법에 대한 제안은 환영 이상입니다. 내가 아는 것 :
ld
기호 재정의 오류와 함께 폭발했습니다) 생성 된 코드가 내 마이크로 아키텍처에 맞게 조정되었다고 생각하지 않습니다.32 비트
.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
편집 : 나는 또한 uint64_t
성능 향상이 있는지 확인하기 위해 컴퓨터에서 유형을 사용하려고했습니다 . 성능은 32 비트보다 약 10 % 빠르며 64 비트 유형을 사용하여 한 번에 두 개의 32 비트 int
유형에서 비트를 되돌 리거나 64 비트의 절반을 실제로 반전했는지 여부 와 거의 동일합니다 . 비트 값. 어셈블리 코드는 다음과 같습니다 (이전의 경우 한 번에 두 개의 32 비트 int
유형에 대한 반전 비트 ).
.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
이 스레드는 최신 CPU에서도 많은 작업 (CPU 사이클)이 필요한 간단한 문제를 다루기 때문에 관심을 끌었습니다. 그리고 어느 날 나는 동일한 ¤ # % "#"문제로 거기에 서있었습니다. 나는 수백만 바이트를 뒤집어 야했다. 그러나 모든 대상 시스템이 최신 Intel 기반 시스템이라는 것을 알고 있으므로 극단적으로 최적화를 시작하겠습니다 !!!
그래서 Matt J의 조회 코드를 기본으로 사용했습니다. 내가 벤치마킹하는 시스템은 i7 haswell 4700eq입니다.
Matt J의 조회 비트 수 4 억 000 바이트 : 약 0.272 초.
그런 다음 인텔의 ISPC 컴파일러가 reverse.c의 산술을 벡터화 할 수 있는지 확인하려고했습니다.
컴파일러가 물건을 찾는 데 도움을주기 위해 많은 노력을 기울 였기 때문에 여기서 찾은 결과를 보지 않을 것입니다. 크게 줄었지만 내 응용 프로그램에는 여전히 너무 느립니다.
그래서 사람들은 내가 세상에서 가장 빠른 인텔 기반 비트 플리퍼를 제시 할 수있게 해줍니다. 에 시계 :
비트 플립 시간 400000000 바이트 : 0.050082 초 !!!!!
// 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는 디버깅을위한 것입니다.
다음은 핵심입니다.
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
이 코드는 32 바이트를 차지하고 니블을 마스킹합니다. 높은 니블은 4만큼 오른쪽으로 이동합니다. 그런 다음 vpshufb와 ymm4 / ymm3을 조회 테이블로 사용합니다. 단일 조회 테이블을 사용할 수는 있지만 니블을 다시 OR하기 전에 왼쪽으로 이동해야합니다.
비트를 뒤집는 더 빠른 방법이 있습니다. 그러나 나는 단일 스레드와 CPU에 묶여있어 이것이 달성 할 수있는 가장 빠릅니다. 더 빠른 버전을 만들 수 있습니까?
Intel C / C ++ Compiler 내장 동등 명령 사용에 대해서는 언급하지 마십시오 ...
pshub
. 왜냐하면 모든 최고의 popcount가 그것으로도 완료 되었기 때문이다! 당신을 위해서가 아니라면 여기에 썼을 것입니다. 명성.
popcnt
에서 tzcnt
, 및 pext
모두에 대한 실행 단위를 갖습니다 . 따라서 모든 처리량 pext
또는 tzcnt
비용이 발생합니다 popcnt
. 데이터가 L1D 캐시에서 핫한 경우 인텔 CPU에서 어레이를 가장 빠르게 계산하는 방법은 AVX2 pshufb를 사용하는 것입니다. (Ryzen은 클록 당 4 개의 popcnt
처리량을 가지므로 아마도 최적이지만 Bulldozer 제품군은 4 개의 클록 popcnt r64,r64
처리량 당 하나를 가지고 있습니다 ... agner.org/optimize ).
이것은 재귀를 좋아하는 사람들을위한 또 다른 솔루션입니다.
아이디어는 간단하다. 입력을 반으로 나누고 두 개의 반쪽을 바꾸고 단일 비트에 도달 할 때까지 계속하십시오.
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
다음은이를 해결하기위한 재귀 함수입니다. (참고 부호없는 정수를 사용 했으므로 sizeof (unsigned int) * 8 비트까지의 입력에 사용할 수 있습니다.
재귀 함수에는 2 개의 매개 변수가 있습니다. 비트를 반전해야하는 값과 값의 비트 수입니다.
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);
}
이것은 출력입니다.
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
int와 마찬가지로 함수 매개 변수에 대해 3을 2로 나누면 1로 내림됩니까?
글쎄, 이것은 확실히 Matt J와 같은 대답은 아니지만 여전히 유용 할 것입니다.
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;
}
이것은 64 비트 숫자의 바이트 (비트가 아닌)를 교환하는 BSWAP라는이 작은 명령이 있다는 것을 제외하고 Matt의 최고의 알고리즘과 정확히 동일합니다. 따라서 b7, b6, b5, b4, b3, b2, b1, b0은 b0, b1, b2, b3, b4, b5, b6, b7이됩니다. 32 비트 숫자로 작업하기 때문에 바이트 스왑 된 숫자를 32 비트 아래로 이동해야합니다. 이것은 우리에게 각 바이트의 8 비트와 짜잔을 교환하는 작업으로 우리를 떠납니다! 우리는 끝났습니다.
타이밍 : 내 컴퓨터에서 Matt의 알고리즘은 시행 당 ~ 0.52 초 안에 실행되었습니다. 시험 당 약 0.42 초 안에 광산이 달렸다. 생각보다 20 % 빠르면 나쁘지 않습니다.
명령어 BSWAP의 사용 가능성에 대해 걱정하는 경우 Wikipedia 는 명령어 BSWAP가 1989 년에 나온 80846에 추가 된 것으로 나열합니다. 내 컴퓨터의 경우 64 비트 레지스터에서만 작동합니다.
이 메소드는 모든 정수 데이터 유형에 대해 동일하게 작동하므로 원하는 바이트 수를 전달하여 사소하게 일반화 할 수 있습니다.
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;
}
그러면 다음과 같이 호출 될 수 있습니다.
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
컴파일러는 여분의 매개 변수를 최적화 할 수 있어야하며 (컴파일러가 함수를 인라인한다고 가정) sizeof(size_t)
오른쪽 시프트는 완전히 제거됩니다. GCC는 적어도 BSWAP를 제거 할 수 없으며 통과하면 오른쪽 이동을 수행 할 수 없습니다 sizeof(char)
.
Anders Cedronius의 답변 은 AVX2를 지원하는 x86 CPU를 가진 사람들에게 훌륭한 솔루션을 제공합니다. AVX를 지원하지 않는 x86 플랫폼 또는 x86 이외의 플랫폼의 경우 다음 구현 중 하나가 제대로 작동합니다.
첫 번째 코드는 클래식 이진 파티셔닝 방법의 변형이며 다양한 ARM 프로세서에 유용한 shift-plus-logic 관용구를 최대한 활용하도록 코딩되었습니다. 또한 각 32 비트 마스크 값을로드하기 위해 여러 명령이 필요한 RISC 프로세서에 유리한 온더 플라이 마스크 생성 기능을 사용합니다. x86 플랫폼 용 컴파일러는 지속적인 전파를 사용하여 런타임보다는 컴파일 타임에 모든 마스크를 계산해야합니다.
/* 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;
}
"컴퓨터 프로그래밍의 기술"4 권에서 D. Knuth는 고전 이진 분할 알고리즘보다 약간 적은 연산이 필요한 비트를 반전시키는 영리한 방법을 보여줍니다. TAOCP에서 찾을 수없는 32 비트 피연산자에 대한 이러한 알고리즘 중 하나 가이 문서 의 Hacker 's Delight 웹 사이트에 나와 있습니다.
/* 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 컴파일러 C / C ++ 컴파일러 13.1.3.198을 사용하면 위의 두 함수가 모두 훌륭한 XMM
레지스터를 자동 벡터화 합니다. 많은 노력 없이도 수동으로 벡터화 할 수 있습니다.
자동 벡터화 된 코드를 사용하는 IvyBridge Xeon E3 1270v2에서 1 억 uint32_t
단어는을 사용하여 0.070 초 brev_classic()
,을 사용하여 0.068 초 에 비트 반전되었습니다 brev_knuth()
. 벤치 마크가 시스템 메모리 대역폭에 의해 제한되지 않도록주의를 기울였습니다.
brev_knuth()
. Hacker 's Delight의 PDF 속성에 따르면이 숫자는 Knuth 자신이 직접 작성한 것 같습니다. 상수가 어떻게 도출되었는지, 또는 임의의 단어 크기에 대한 상수 및 변화 인자를 도출하는 방법에 대해 충분히 설명하기 위해 TAOCP의 기본 설계 원칙에 대한 Knuth의 설명을 이해했다고 주장 할 수 없습니다.
비트 배열이 있다고 가정하면 다음과 같은 이점이 있습니다. 1. MSB부터 비트를 하나씩 스택에 넣습니다. 2.이 스택의 팝 비트를 다른 어레이 (또는 공간을 절약하려는 경우 동일한 어레이)로 팝하고 첫 번째 팝된 비트를 MSB에 배치 한 다음 그보다 덜 중요한 비트로 진행합니다.
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();
}
이것은 인간에게는 직업이 아닙니다! ...하지만 기계에 완벽
이 질문이 처음 제기 된 지 6 년이 지난 2015 년입니다. 그 후 컴파일러는 우리의 주인이되었으며, 인간으로서의 우리의 일은 단지 그들을 돕는 것입니다. 머신에 의도를 부여하는 가장 좋은 방법은 무엇입니까?
비트 리버설 (bit-reversal)은 매우 일반적이기 때문에 x86의 ISA가 왜 커지고 있는지에 대한 지침이없는 이유를 궁금해해야합니다.
그 이유 : 컴파일러에 진정한 간결한 의도를 부여하면 비트 리버설은 ~ 20 CPU 사이클 만 소요 됩니다 . reverse ()를 작성하고 사용하는 방법을 보여 드리겠습니다.
#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;
}
Clang 버전> = 3.6, -O3, -march = native (Haswell에서 테스트)로이 샘플 프로그램을 컴파일하면 새로운 AVX2 명령어를 사용하여 11 초의 런타임으로 ~ 10 억 reverse ()를 처리 하여 아트 워크 품질의 코드를 얻을 수 있습니다. 2GHz가 가정하면 20 CPU 사이클을 달콤한 것으로 가정하면 0.5 ns CPU 사이클로 reverse () 당 ~ 10 ns입니다.
주의 사항 :이 샘플 코드는 몇 년 동안 괜찮은 벤치 마크가되어야하지만, 컴파일러가 main ()을 최적화하여 실제 결과를 계산하는 대신 최종 결과를 인쇄 할 수있을 정도로 똑똑해지면 결국 그 시대를 보여줄 것입니다. 그러나 지금은 reverse ()를 보여줍니다.
Bit-reversal is so common...
나는 그것에 대해 모른다. 나는 거의 매일 비트 수준의 데이터를 처리하는 코드를 사용하며, 이러한 특정 요구가 있었음을 기억할 수 없습니다. 어떤 시나리오에서 필요합니까? -자체적으로 해결하는 것은 흥미로운 문제가 아닙니다.
물론 비트 트위들 링 핵의 명백한 출처는 다음과 같습니다. http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
나는 그것이 C가 아니라 asm이라는 것을 안다.
var1 dw 0f0f0
clc
push ax
push cx
mov cx 16
loop1:
shl var1
shr ax
loop loop1
pop ax
pop cx
이것은 캐리 비트와 함께 작동하므로 플래그를 저장할 수도 있습니다.
rcl
CF를로 바꾸고 싶다고 생각합니다 . (또는 ). 이 수정으로도 느린 명령을 사용 하고 메모리에 보관 하면 엄청나게 느립니다 ! 실제로 이것이 AX에서 출력을 생성한다고 생각되지만 결과 위에 AX의 이전 값을 저장 / 복원합니다. var1
shl
adc dx,dx
loop
var1
이것은 기본적으로 첫 번째 "reverse ()"와 동일하지만 64 비트이며 명령 스트림에서로드 할 즉각적 마스크가 하나만 필요합니다. GCC는 점프없이 코드를 생성하므로 매우 빠릅니다.
#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;
}
나는 명백한 원시 회전이 얼마나 빠를 지 궁금했다. 내 컴퓨터 (i7 @ 2600)에서 1,500,150,000 반복의 평균은 27.28 ns
(131,071 64 비트 정수의 임의 세트 이상)입니다.
장점 : 필요한 메모리 양이 적고 코드가 간단합니다. 나는 그것이 그렇게 크지 않다고 말할 것입니다. 필요한 시간은 모든 입력에 대해 예측 가능하고 일정합니다 (128 산술 SHIFT 연산 + 64 논리 AND 연산 + 64 논리 OR 연산).
나는 @Matt J가 얻은 최고의 시간과 비교했습니다. 그의 대답을 올바르게 읽으면 그가 얻은 최고는 반복에 0.631739
대한 초였습니다 1,000,000
. 이것은 평균 631 ns
회전 당입니다.
내가 사용한 코드 스 니펫은 다음과 같습니다.
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);
}
표준 템플릿 라이브러리를 사용할 수 있습니다. 위에서 언급 한 코드보다 느릴 수 있습니다. 그러나 더 명확하고 이해하기 쉬운 것 같습니다.
#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;
}
일반적인
C 코드. 1 바이트 입력 데이터 수를 예로 사용합니다.
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);
다음은 어떻습니까 :
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;
}
작고 쉬움 (32 비트 만 해당).
나는 이것이 비트를 뒤집는 가장 간단한 방법 중 하나라고 생각했다. 이 논리에 결함이 있으면 알려주십시오. 기본적으로이 논리에서 비트의 위치 값을 확인합니다. 반전 위치에서 값이 1이면 비트를 설정하십시오.
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
항상 2의 거듭 제곱이지만 컴파일러는이를 입증하지 않고 비트 스캔 / 시프트로 바꿉니다.
내가 아는 가장 간단한 방법은 다음과 같습니다. MSB
입력되고 LSB
'역전 된'출력입니다.
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
숫자가 적을 때 빠르게 종료되는 다른 루프 기반 솔루션 (여러 유형의 경우 C ++에서)
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;
}
또는 부호없는 int의 경우 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;
}
다른 많은 게시물은 속도에 대해 우려하는 것 같습니다 (즉 가장 빠름 = 가장 빠름). 단순성은 어떻습니까? 치다:
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;
}
영리한 컴파일러가 당신을 위해 최적화되기를 바랍니다.
더 긴 비트 목록 (비트 포함 sizeof(char) * n
) 을 반대로 바꾸려면 이 함수를 사용하여 다음을 얻을 수 있습니다.
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]);
}
}
이것은 [10000000, 10101010]을 [01010101, 00000001]로 되돌릴 것입니다.
ith_bit = (c >> i) & 1
. 또한 대상 레지스터에서 n 번째 비트를 설정하기 위해 reversed_char
x86에서 sub something
/ bts reg,reg
로 컴파일되기를 기대하지 않는 한 비트를 이동하는 대신 이동하여 SUB를 저장하십시오 .
의사 코드의 비트 반전
소스-> 반전 될 바이트 b00101100 대상-> 반전 됨, 부호없는 유형이어야하므로 부호 비트가 전파되지 않음
원본에 영향을 미치지 않도록 임시로 복사하고 부호 비트가 자동으로 이동되지 않도록 부호없는 유형이어야합니다.
bytecopy = b0010110
LOOP8 : // 바이트 카피가 <0 (음수)이면이 8 회 테스트
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.
나의 간단한 해결책
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
? 또한, 그 마법 상수는 * 4
무엇입니까? 그렇 CHAR_BIT / 2
습니까?
이것은 32 비트 용이며 8 비트를 고려하면 크기를 변경해야합니다.
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 순서로 입력 정수 "num"을 읽고 MSB-> LSB 순서로 num_reverse에 저장합니다.