The page Bit Twiddling Hacks shows how to reverse bits in an integer (e.g, reverse LSB with MSB bits or reading bits from the opposite direction), but seems it doesn't explain anything why that works.
Here I try to explain it step by step. Let start with a 8-bit char first. Assume each letter in ABCDEFGH represents each bits in the char (e.g, A is for MSB etc.).
x = ABCDEFGH
ABCDEFGH
10101010 (or 0xAA)
-------- &
A0C0E0G0 ---- >> 1 : 0A0C0E0G
ABCDEFGH
01010101 (or 0x55)
-------- &
0B0D0F0H ---- << 1 : B0D0F0H0
0A0C0E0G
B0D0F0H0
-------- |
BADCFEHG ---> stored as a new x, then we do next operation
BADCFEHG
11001100 (or 0xCC)
--------- &
BA00FE00 ---- >> 2: 00BA00FE
BADCFEHG
00110011 (or 0x33)
--------- &
00DC00HG ---- << 2: DC00HG00
00BA00FE
DC00HG00
-------- |
DCBAHGFE
Last, we operate the similar thing on the high and low nibbes:
DCBAHGFE
11110000 (or 0xF0)
-------- &
DCBA0000 ==> >> 4 = 0000DCBA
DCBAHGFE
00001111 (or 0x0F)
-------- &
0000HGFE ==> << 4 = HGFE0000
0000DCBA
HGFE0000
--------|
HGFEDCBA
Finally, we now get HGFEDCBA which is the reversed bits of ABCDEFGH! To summarize, to reverse bits in an octet, we do operations:
x = (x & 0xAA >> 1) | (x & 0x55 << 1);
x = (x & 0xCC >> 2) | (x & 0x33 << 2);
x = (x & 0xF0 >> 4) | (x & 0x0F >> 4);
For operation on 32-bit data, we do the similar one, except we do with 32-bit mask (instead of 10101010 binary or 0xAA, we mask with 0xAAAAAAAA) plus extra two pair of masks (0xF0F0F0F0 and 0x0F0F0F0F, 0xFF00FF00 and 0x00FF00FF) with shift left/right 4, 8 and 16 bit.
So, bit-reversal computation or algorithm for 32-bit integer:
unsigned int reverse_bits(unsigned int x)
{
x = ((x & 0xA0A0A0A0) >> 1) | ((x & 0x05050505) << 1);
x = ((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2);
Here I try to explain it step by step. Let start with a 8-bit char first. Assume each letter in ABCDEFGH represents each bits in the char (e.g, A is for MSB etc.).
x = ABCDEFGH
ABCDEFGH
10101010 (or 0xAA)
-------- &
A0C0E0G0 ---- >> 1 : 0A0C0E0G
ABCDEFGH
01010101 (or 0x55)
-------- &
0B0D0F0H ---- << 1 : B0D0F0H0
0A0C0E0G
B0D0F0H0
-------- |
BADCFEHG ---> stored as a new x, then we do next operation
BADCFEHG
11001100 (or 0xCC)
--------- &
BA00FE00 ---- >> 2: 00BA00FE
BADCFEHG
00110011 (or 0x33)
--------- &
00DC00HG ---- << 2: DC00HG00
00BA00FE
DC00HG00
-------- |
DCBAHGFE
Last, we operate the similar thing on the high and low nibbes:
DCBAHGFE
11110000 (or 0xF0)
-------- &
DCBA0000 ==> >> 4 = 0000DCBA
DCBAHGFE
00001111 (or 0x0F)
-------- &
0000HGFE ==> << 4 = HGFE0000
0000DCBA
HGFE0000
--------|
HGFEDCBA
Finally, we now get HGFEDCBA which is the reversed bits of ABCDEFGH! To summarize, to reverse bits in an octet, we do operations:
x = (x & 0xAA >> 1) | (x & 0x55 << 1);
x = (x & 0xCC >> 2) | (x & 0x33 << 2);
x = (x & 0xF0 >> 4) | (x & 0x0F >> 4);
For operation on 32-bit data, we do the similar one, except we do with 32-bit mask (instead of 10101010 binary or 0xAA, we mask with 0xAAAAAAAA) plus extra two pair of masks (0xF0F0F0F0 and 0x0F0F0F0F, 0xFF00FF00 and 0x00FF00FF) with shift left/right 4, 8 and 16 bit.
So, bit-reversal computation or algorithm for 32-bit integer:
unsigned int reverse_bits(unsigned int x)
{
x = ((x & 0xA0A0A0A0) >> 1) | ((x & 0x05050505) << 1);
x = ((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2);
x = ((x & 0x0F0F0F0F) >> 4) | ((x & 0xF0F0F0F0) << 4);
x = ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8);
return (x >> 16) | (x << 16);
x = ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8);
return (x >> 16) | (x << 16);
}
No comments:
Post a Comment