在 C/C + + 中,逆转字节中位的顺序最简单的方法是什么?

虽然有多种方法可以逆转字节中的位顺序,但我很好奇,对于开发人员来说,什么是最“简单”的实现。我说的倒车是指:

1110 -> 0111
0010 -> 0100

这与 这个 PHP 问题相似,但不是重复。

这是类似的,但不是一个重复的 这个 C 问题。这个问题要求开发人员实现最简单的方法。“最佳算法”与内存和 CPU 性能有关。

243594 次浏览

如果您讨论的是一个字节,那么表查找可能是最好的选择,除非由于某种原因您没有256个字节可用。

你可能会对 std::vector<bool>(比特打包)和 std::bitset感兴趣

这应该是最简单的要求。

#include <iostream>
#include <bitset>
using namespace std;
int main() {
bitset<8> bs = 5;
bitset<8> rev;
for(int ii=0; ii!= bs.size(); ++ii)
rev[bs.size()-ii-1] = bs[ii];
cerr << bs << " " << rev << endl;
}

其他选择可能更快。

编辑: 我欠你一个使用 std::vector<bool>的解决方案

#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<bool> b{0,0,0,0,0,1,0,1};
reverse(b.begin(), b.end());
copy(b.begin(), b.end(), ostream_iterator<int>(cerr));
cerr << endl;
}

第二个示例需要 c + + 0x 扩展(用 {...}初始化数组)。使用 bitset或者 std::vector<bool>(或者 boost::dynamic_bitset)的优点是,您不受字节或单词的限制,而是可以反转任意数量的位。

高温

有关许多解决方案,请参阅 无聊的工作。从那里复制显然很容易实现。 =)

例如(在32位 CPU 上) :

uint8_t b = byte_to_reverse;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;

如果说“简单易行”是指在考试或面试中不需要参考就可以完成的事情,那么最安全的办法可能是将一个个比特以相反的顺序复制到另一个变量中(已经在其他答案中显示出来了)。

template <typename T>
T reverse(T n, size_t b = sizeof(T) * CHAR_BIT)
{
assert(b <= std::numeric_limits<T>::digits);


T rv = 0;


for (size_t i = 0; i < b; ++i, n >>= 1) {
rv = (rv << 1) | (n & 0x01);
}


return rv;
}

编辑:

将其转换为具有可选位数的模板

查表还是

uint8_t rev_byte(uint8_t x) {
uint8_t y;
uint8_t m = 1;
while (m) {
y >>= 1;
if (m&x) {
y |= 0x80;
}
m <<=1;
}
return y;
}

编辑

寻找 给你的其他解决方案,可能会更好地为您工作

这应该会奏效:

unsigned char reverse(unsigned char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}

首先,左边的四位与右边的四位进行交换。然后交换所有相邻对,然后交换所有相邻单位。这将导致一个相反的顺序。

虽然可能不是可移植的,但我会使用汇编语言。
许多汇编语言都有指令将进位标志旋转一点,并将进位标志旋转到单词(或字节)。

算法是:

for each bit in the data type:
rotate bit into carry flag
rotate carry flag into destination.
end-for

由于 C 和 C + + 不支持旋转进位和旋转从进位,所以用于此的高级语言代码要复杂得多。进行旗必须建模。

例如,编辑: 汇编语言

;  Enter with value to reverse in R0.
;  Assume 8 bits per byte and byte is the native processor type.
LODI, R2  8       ; Set up the bit counter
Loop:
RRC, R0           ; Rotate R0 right into the carry bit.
RLC, R1           ; Rotate R1 left, then append carry bit.
DJNZ, R2  Loop    ; Decrement R2 and jump if non-zero to "loop"
LODR, R0  R1      ; Move result into R0.

我认为查找表必须是最简单的方法之一。但是,您不需要完整的查找表。

//Index 1==0b0001 => 0b1000
//Index 7==0b0111 => 0b1110
//etc
static unsigned char lookup[16] = {
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };


uint8_t reverse(uint8_t n) {
// Reverse the top and bottom nibble then swap them.
return (lookup[n&0b1111] << 4) | lookup[n>>4];
}


// Detailed breakdown of the math
//  + lookup reverse of bottom nibble
//  |       + grab bottom nibble
//  |       |        + move bottom result into top nibble
//  |       |        |     + combine the bottom and top results
//  |       |        |     | + lookup reverse of top nibble
//  |       |        |     | |       + grab top nibble
//  V       V        V     V V       V
// (lookup[n&0b1111] << 4) | lookup[n>>4]

这相当简单,可视化地编码和验证。
最终,这甚至可能比一个完整的表还要快。位 Arith 很便宜,而且表很容易安装在缓存线上。

在实现任何算法解决方案之前,请检查汇编语言,了解您正在使用的 CPU 架构。您的体系结构可能包括处理这样的按位操作的指令(还有什么比单个汇编指令更简单?).

如果没有这样的指令,那么我建议使用查找表路由。您可以编写一个脚本/程序来为您生成表,而且查找操作比这里的任何位反转算法都要快(代价是必须将查找表存储在某个地方)。

最简单的方法可能是在循环中迭代位位置:

unsigned char reverse(unsigned char c) {
int shift;
unsigned char result = 0;
for (shift = 0; shift < CHAR_BIT; shift++) {
if (c & (0x01 << shift))
result |= (0x80 >> shift);
}
return result;
}

一种较慢但更简单的实施方式:

static int swap_bit(unsigned char unit)
{
/*
* swap bit[7] and bit[0]
*/
unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));
unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01))) | (unit & 0xfe));
unit = (((((unit & 0x80) >> 7) ^ (unit & 0x01)) << 7) | (unit & 0x7f));


/*
* swap bit[6] and bit[1]
*/
unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));
unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02))) | (unit & 0xfd));
unit = (((((unit & 0x40) >> 5) ^ (unit & 0x02)) << 5) | (unit & 0xbf));


/*
* swap bit[5] and bit[2]
*/
unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));
unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04))) | (unit & 0xfb));
unit = (((((unit & 0x20) >> 3) ^ (unit & 0x04)) << 3) | (unit & 0xdf));


/*
* swap bit[4] and bit[3]
*/
unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));
unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08))) | (unit & 0xf7));
unit = (((((unit & 0x10) >> 1) ^ (unit & 0x08)) << 1) | (unit & 0xef));


return unit;
}

由于没有人发布完整的表查找解决方案,以下是我的解决方案:

unsigned char reverse_byte(unsigned char x)
{
static const unsigned char table[] = {
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,
};
return table[x];
}

两句台词:

for(i=0;i<8;i++)
reversed |= ((original>>i) & 0b1)<<(7-i);

或者万一你对“0b1”部分有问题:

for(i=0;i<8;i++)
reversed |= ((original>>i) & 1)<<(7-i);

“原始”是要反转的字节。 “反转”是结果,初始化为0。

用0xFF 异或字节怎么样。

无符号字符反转(无符号字符 b){ B ^ = 0xFF; 返回 b; }

这个怎么样。

int value = 0xFACE;


value = ((0xFF & value << 8) | (val >> 8);

这能快速解决吗?

int byte_to_be_reversed =
((byte_to_be_reversed>>7)&0x01)|((byte_to_be_reversed>>5)&0x02)|
((byte_to_be_reversed>>3)&0x04)|((byte_to_be_reversed>>1)&0x08)|
((byte_to_be_reversed<<7)&0x80)|((byte_to_be_reversed<<5)&0x40)|
((byte_to_be_reversed<<3)&0x20)|((byte_to_be_reversed<<1)&0x10);

摆脱了使用 for 循环的麻烦!但请专家告诉我,这是否有效和更快?

对于非常有限的 常数,8位输入,此方法在运行时不占用内存或 CPU:

#define MSB2LSB(b) (((b)&1?128:0)|((b)&2?64:0)|((b)&4?32:0)|((b)&8?16:0)|((b)&16?8:0)|((b)&32?4:0)|((b)&64?2:0)|((b)&128?1:0))

我将其用于 ARINC-429,其中标签的位顺序(endianness)与单词的其余部分相反。标签常常是一个常数,通常是八进制。

下面是我如何使用它来定义一个常量,因为规范将这个标签定义为 big-endian 205八进制。

#define LABEL_HF_COMM MSB2LSB(0205)

更多例子:

assert(0b00000000 == MSB2LSB(0b00000000));
assert(0b10000000 == MSB2LSB(0b00000001));
assert(0b11000000 == MSB2LSB(0b00000011));
assert(0b11100000 == MSB2LSB(0b00000111));
assert(0b11110000 == MSB2LSB(0b00001111));
assert(0b11111000 == MSB2LSB(0b00011111));
assert(0b11111100 == MSB2LSB(0b00111111));
assert(0b11111110 == MSB2LSB(0b01111111));
assert(0b11111111 == MSB2LSB(0b11111111));
assert(0b10101010 == MSB2LSB(0b01010101));

我发现下面的解决方案比我在这里看到的其他比特摆弄算法更简单。

unsigned char reverse_byte(char a)
{


return ((a & 0x1)  << 7) | ((a & 0x2)  << 5) |
((a & 0x4)  << 3) | ((a & 0x8)  << 1) |
((a & 0x10) >> 1) | ((a & 0x20) >> 3) |
((a & 0x40) >> 5) | ((a & 0x80) >> 7);
}

它获取字节中的每一位,并相应地将其从第一位移动到最后一位。

说明:

   ((a & 0x1) << 7) //get first bit on the right and shift it into the first left position
| ((a & 0x2) << 5) //add it to the second bit and shift it into the second left position
//and so on
#include <stdio.h>
#include <stdlib.h>


int main()
{
int i;
unsigned char rev = 0x70 ; // 0b01110000
unsigned char tmp = 0;


for(i=0;i<8;i++)
{
tmp |= ( ((rev & (1<<i))?1:0) << (7-i));
}
rev = tmp;


printf("%x", rev);       //0b00001110 binary value of given number
return 0;
}

这是一个古老的问题,但似乎没有人显示出明确的容易的方式(最接近的是 edW)。我使用 C # 来测试这个,但是在这个例子中没有什么是不能在 C 中轻松完成的。

void PrintBinary(string prompt, int num, int pad = 8)
{
Debug.WriteLine($"{prompt}: {Convert.ToString(num, 2).PadLeft(pad, '0')}");
}


int ReverseBits(int num)
{
int result = 0;
int saveBits = num;
for (int i = 1; i <= 8; i++)
{
// Move the result one bit to the left
result = result << 1;


//PrintBinary("saveBits", saveBits);


// Extract the right-most bit
var nextBit = saveBits & 1;


//PrintBinary("nextBit", nextBit, 1);


// Add our extracted bit to the result
result = result | nextBit;


//PrintBinary("result", result);


// We're done with that bit, rotate it off the right
saveBits = saveBits >> 1;


//Debug.WriteLine("");
}


return result;
}


void PrintTest(int nextNumber)
{
var result = ReverseBits(nextNumber);


Debug.WriteLine("---------------------------------------");
PrintBinary("Original", nextNumber);
PrintBinary("Reverse", result);
}


void Main()
{
// Calculate the reverse for each number between 1 and 255
for (int x = 250; x < 256; x++)
PrintTest(x);
}
#define BITS_SIZE 8


int
reverseBits ( int a )
{
int rev = 0;
int i;


/* scans each bit of the input number*/
for ( i = 0; i < BITS_SIZE - 1; i++ )
{
/* checks if the bit is 1 */
if ( a & ( 1 << i ) )
{
/* shifts the bit 1, starting from the MSB to LSB
* to build the reverse number
*/
rev |= 1 << ( BITS_SIZE - 1 ) - i;
}
}


return rev;
}

这一个是基于一个 BobStein-Visibone提供

#define reverse_1byte(b)    ( ((uint8_t)b & 0b00000001) ? 0b10000000 : 0 ) | \
( ((uint8_t)b & 0b00000010) ? 0b01000000 : 0 ) | \
( ((uint8_t)b & 0b00000100) ? 0b00100000 : 0 ) | \
( ((uint8_t)b & 0b00001000) ? 0b00010000 : 0 ) | \
( ((uint8_t)b & 0b00010000) ? 0b00001000 : 0 ) | \
( ((uint8_t)b & 0b00100000) ? 0b00000100 : 0 ) | \
( ((uint8_t)b & 0b01000000) ? 0b00000010 : 0 ) | \
( ((uint8_t)b & 0b10000000) ? 0b00000001 : 0 )

我真的很喜欢这一个,因为编译器自动处理的工作,因此不需要进一步的资源。

这也可以扩展到16位..。

#define reverse_2byte(b)    ( ((uint16_t)b & 0b0000000000000001) ? 0b1000000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000000010) ? 0b0100000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000000100) ? 0b0010000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000001000) ? 0b0001000000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000010000) ? 0b0000100000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000000100000) ? 0b0000010000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000001000000) ? 0b0000001000000000 : 0 ) | \
( ((uint16_t)b & 0b0000000010000000) ? 0b0000000100000000 : 0 ) | \
( ((uint16_t)b & 0b0000000100000000) ? 0b0000000010000000 : 0 ) | \
( ((uint16_t)b & 0b0000001000000000) ? 0b0000000001000000 : 0 ) | \
( ((uint16_t)b & 0b0000010000000000) ? 0b0000000000100000 : 0 ) | \
( ((uint16_t)b & 0b0000100000000000) ? 0b0000000000010000 : 0 ) | \
( ((uint16_t)b & 0b0001000000000000) ? 0b0000000000001000 : 0 ) | \
( ((uint16_t)b & 0b0010000000000000) ? 0b0000000000000100 : 0 ) | \
( ((uint16_t)b & 0b0100000000000000) ? 0b0000000000000010 : 0 ) | \
( ((uint16_t)b & 0b1000000000000000) ? 0b0000000000000001 : 0 )
  xor ax,ax
xor bx,bx
mov cx,8
mov al,original_byte!
cycle:   shr al,1
jnc not_inc
inc bl
not_inc: test cx,cx
jz,end_cycle
shl bl,1
loop cycle
end_cycle:

反向字节将在 BL寄存器

typedef struct
{
uint8_t b0:1;
uint8_t b1:1;
uint8_t b2:1;
uint8_t b3:1;
uint8_t b4:1;
uint8_t b5:1;
uint8_t b6:1;
uint8_t b7:1;
} bits_t;


uint8_t reverse_bits(uint8_t src)
{
uint8_t dst = 0x0;
bits_t *src_bits = (bits_t *)&src;
bits_t *dst_bits = (bits_t *)&dst;


dst_bits->b0 = src_bits->b7;
dst_bits->b1 = src_bits->b6;
dst_bits->b2 = src_bits->b5;
dst_bits->b3 = src_bits->b4;
dst_bits->b4 = src_bits->b3;
dst_bits->b5 = src_bits->b2;
dst_bits->b6 = src_bits->b1;
dst_bits->b7 = src_bits->b0;


return dst;
}

这个简单的函数使用一个掩码来测试输入字节中的每个位,并将其转换为一个移动输出:

char Reverse_Bits(char input)
{
char output = 0;


for (unsigned char mask = 1; mask > 0; mask <<= 1)
{
output <<= 1;


if (input & mask)
output |= 1;
}


return output;
}

我觉得这很简单

uint8_t reverse(uint8_t a)
{
unsigned w = ((a << 7) & 0x0880) | ((a << 5) & 0x0440) | ((a << 3) & 0x0220) | ((a << 1) & 0x0110);
return static_cast<uint8_t>(w | (w>>8));
}

或者

uint8_t reverse(uint8_t a)
{
unsigned w = ((a & 0x11) << 7) | ((a & 0x22) << 5) | ((a & 0x44) << 3) | ((a & 0x88) << 1);
return static_cast<uint8_t>(w | (w>>8));
}
unsigned char c ; // the original
unsigned char u = // the reversed
c>>7&0b00000001 |
c<<7&0b10000000 |
c>>5&0b00000010 |
c<<5&0b01000000 |
c>>3&0b00000100 |
c<<3&0b00100000 |
c>>1&0b00001000 |
c<<1&0b00010000 ;


Explanation: exchanged bits as per the arrows below.
01234567
<------>
#<---->#
##<-->##
###<>###
#include <stdio.h>
#include <stdlib.h>


#define BIT0 (0x01)
#define BIT1 (0x02)
#define BIT2 (0x04)
#define BIT3 (0x08)
#define BIT4 (0x10)
#define BIT5 (0x20)
#define BIT6 (0x40)
#define BIT7 (0x80)


#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c\n"


#define BITETOBINARY(byte) \
(byte & BIT7 ? '1' : '0'), \
(byte & BIT6 ? '1' : '0'), \
(byte & BIT5 ? '1' : '0'), \
(byte & BIT4 ? '1' : '0'), \
(byte & BIT3 ? '1' : '0'), \
(byte & BIT2 ? '1' : '0'), \
(byte & BIT1 ? '1' : '0'), \
(byte & BIT0 ? '1' : '0') \


#define BITETOBINARYREVERSE(byte) \
(byte & BIT0 ? '1' : '0'), \
(byte & BIT1 ? '1' : '0'), \
(byte & BIT2 ? '1' : '0'), \
(byte & BIT3 ? '1' : '0'), \
(byte & BIT4 ? '1' : '0'), \
(byte & BIT5 ? '1' : '0'), \
(byte & BIT6 ? '1' : '0'), \
(byte & BIT7 ? '1' : '0') \






int main()
{


int i,j,c;


i |= BIT2|BIT7;


printf("0x%02X\n",i);


printf(BYTE_TO_BINARY_PATTERN,BITETOBINARY(i));


printf("Reverse");


printf(BYTE_TO_BINARY_PATTERN,BITETOBINARYREVERSE(i));


return 0;
}

我会把我的解决方案集中起来,因为到目前为止我在答案中找不到任何类似的东西。它可能有点过度设计,但是它在编译时使用 C + + 14 std::index_sequence生成查找表。

#include <array>
#include <utility>


constexpr unsigned long reverse(uint8_t value) {
uint8_t result = 0;
for (std::size_t i = 0, j = 7; i < 8; ++i, --j) {
result |= ((value & (1 << j)) >> j) << i;
}
return result;
}


template<size_t... I>
constexpr auto make_lookup_table(std::index_sequence<I...>)
{
return std::array<uint8_t, sizeof...(I)>{reverse(I)...};
}


template<typename Indices = std::make_index_sequence<256>>
constexpr auto bit_reverse_lookup_table()
{
return make_lookup_table(Indices{});
}


constexpr auto lookup = bit_reverse_lookup_table();


int main(int argc)
{
return lookup[argc];
}

Https://godbolt.org/z/csuwhf

下面是一个简单易读的解决方案,可移植到所有符合标准的平台,包括那些具有 sizeof(char) == sizeof(int)的平台:

#include <limits.h>


unsigned char reverse(unsigned char c) {
int shift;
unsigned char result = 0;


for (shift = 0; shift < CHAR_BIT; shift++) {
result <<= 1;
result |= c & 1;
c >>= 1;
}
return result;
}

我知道这个问题已经过时了,但我仍然认为这个主题与某些目的有关,这里有一个非常好的版本,是可读的。我不能说它是最快或最有效的,但它应该是最干净的。我还包含了一个助手函数,可以轻松地显示位模式。此函数使用一些标准库函数,而不是编写您自己的位操作器。

#include <algorithm>
#include <bitset>
#include <exception>
#include <iostream>
#include <limits>
#include <string>


// helper lambda function template
template<typename T>
auto getBits = [](T value) {
return std::bitset<sizeof(T) * CHAR_BIT>{value};
};


// Function template to flip the bits
// This will work on integral types such as int, unsigned int,
// std::uint8_t, 16_t etc. I did not test this with floating
// point types. I chose to use the `bitset` here to convert
// from T to string as I find it easier to use than some of the
// string to type or type to string conversion functions,
// especially when the bitset has a function to return a string.
template<typename T>
T reverseBits(T& value) {
static constexpr std::uint16_t bit_count = sizeof(T) * CHAR_BIT;


// Do not use the helper function in this function!
auto bits = std::bitset<bit_count>{value};
auto str = bits.to_string();
std::reverse(str.begin(), str.end());
bits = std::bitset<bit_count>(str);
return static_cast<T>( bits.to_ullong() );
}


// main program
int main() {
try {
std::uint8_t value = 0xE0; // 1110 0000;
std::cout << +value << '\n'; // don't forget to promote unsigned char
// Here is where I use the helper function to display the bit pattern
auto bits = getBits<std::uint8_t>(value);
std::cout << bits.to_string() << '\n';


value = reverseBits(value);
std::cout << +value << '\n'; // + for integer promotion


// using helper function again...
bits = getBits<std::uint8_t>(value);
std::cout << bits.to_string() << '\n';


} catch(const std::exception& e) {
std::cerr << e.what();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

它给出如下输出。

224
11100000
7
00000111

假设您的编译器允许 未签名长期长期:

unsigned char reverse(unsigned char b) {
return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
}

在这里发现的

这一个帮助我与8x8点矩阵阵列集。

uint8_t mirror_bits(uint8_t var)
{
uint8_t temp = 0;
if ((var & 0x01))temp |= 0x80;
if ((var & 0x02))temp |= 0x40;
if ((var & 0x04))temp |= 0x20;
if ((var & 0x08))temp |= 0x10;


if ((var & 0x10))temp |= 0x08;
if ((var & 0x20))temp |= 0x04;
if ((var & 0x40))temp |= 0x02;
if ((var & 0x80))temp |= 0x01;


return temp;
}

根据你所说的“最简单的方法”,有许多方法可以逆转位。


旋转反向

可能最符合逻辑的方法是在第一位 (n & 1)上应用掩码时旋转字节:

unsigned char reverse_bits(unsigned char b)
{
unsigned char   r = 0;
unsigned        byte_len = 8;


while (byte_len--) {
r = (r << 1) | (b & 1);
b >>= 1;
}
return r;
}
  1. 因为 unsigner 字符的长度是1字节,等于8位,这意味着我们将扫描每个位 while (byte_len--)

  2. 我们首先用 (b & 1)检查 b 是否在最右边一点; 如果是这样,我们用 |设置 r 上的位1,并将它向左移动1位 R 乘以2再乘以 (r << 1)

  3. 然后我们将无符号字符 b 除以2再用 b >>=1擦除 位于变量 b 最右边的位。 提醒一下,b > = 1; 等于 b/= 2;


一行倒车

这个解决方案归功于 富 Schroeppel 在编程技巧部分

unsigned char reverse_bits3(unsigned char b)
{
return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
}
  1. 乘法操作(b * 0x02020202ULL)创建5个8位字节模式的独立副本,以分散成64位值。

  2. AND 操作(& 0x010884422010ULL)选择 相对于每个10位位组的正确(反向)位置。

  3. 将乘法和 AND 操作一起从原始位复制位 字节,所以它们每个只出现在10位集中的一个中。 来自原始字节的位的反向位置与它们的 任意10位集内的相对位置。

  4. 最后一步(% 0x3ff) ,其中包括模除以2 ^ 10-1 将每组10位合并在一起的效果 (从位置0-9,10-19,20-29,...)在64位值中。 它们不重叠,所以加法步骤是模的基础 部门的行为类似于 OR 操作。


分而治之

unsigned char reverse(unsigned char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}

这是最受争议的答案,尽管有一些解释,我认为对于大多数人来说,很难想象0xF0,0xCC,0xAA,0x0F,0x33和0x55到底意味着什么。

它没有利用“0b”的优势,因为它是 海湾合作委员会延期,而且是在2014年12月发布的 C + + 14标准中包含的,所以这个答案可以追溯到2010年4月

整数常量可以写成二进制常量,包括一个由“0”和“1”数字组成的序列,前缀为“0B”或“0B”。这在位级操作频繁的环境(如微控制器)中尤其有用。

请检查下面的代码片段,以便更好地记住和理解这个我们移动一半一半的解决方案:

unsigned char reverse(unsigned char b) {
b = (b & 0b11110000) >> 4 | (b & 0b00001111) << 4;
b = (b & 0b11001100) >> 2 | (b & 0b00110011) << 2;
b = (b & 0b10101010) >> 1 | (b & 0b01010101) << 1;
return b;
}

注意: >> 4是因为在1字节中有8位,这是一个无符号字符,所以我们想取另一半,以此类推。

我们可以很容易地将这个解决方案应用到4个字节,只需要两行额外的代码,并遵循相同的逻辑。由于这两种掩模相辅相成,我们甚至可以使用 ~ 来切换位和节省一些墨水:

uint32_t reverse_integer_bits(uint32_t b) {
uint32_t mask = 0b11111111111111110000000000000000;
b = (b & mask) >> 16 | (b & ~mask) << 16;
mask = 0b11111111000000001111111100000000;
b = (b & mask) >> 8 | (b & ~mask) << 8;
mask = 0b11110000111100001111000011110000;
b = (b & mask) >> 4 | (b & ~mask) << 4;
mask = 0b11001100110011001100110011001100;
b = (b & mask) >> 2 | (b & ~mask) << 2;
mask = 0b10101010101010101010101010101010;
b = (b & mask) >> 1 | (b & ~mask) << 1;
return b;
}

[ C + + Only ]反转任何未签名(模板)

上面的逻辑可以用一个循环来概括,这个循环适用于任何类型的无符号:

template <class T>
T reverse_bits(T n) {
short bits = sizeof(n) * 8;
T mask = ~T(0); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;
    

while (bits >>= 1) {
mask ^= mask << (bits); // will convert mask to 0b00000000000000001111111111111111;
n = (n & ~mask) >> bits | (n & mask) << bits; // divide and conquer
}


return n;
}

只有 C + + 17

你可以使用一个表来存储 每个字节的反向值(i * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff,通过一个 lambda 初始化(你需要用 g++ -std=c++1z编译它,因为它只能在 C + + 17之后运行) ,然后返回表中的值,这个值会给你相应的反向位:

#include <cstdint>
#include <array>


uint8_t reverse_bits(uint8_t n) {
static constexpr array<uint8_t, 256> table{[]() constexpr{
constexpr size_t SIZE = 256;
array<uint8_t, SIZE> result{};


for (size_t i = 0; i < SIZE; ++i)
result[i] = (i * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff;
return result;
}()};


return table[n];
}

Main.cpp

自己试试,包括以下功能:

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


template <class T>
void print_binary(T n)
{   T mask = 1ULL << ((sizeof(n) * 8) - 1);  // will set the most significant bit
for(; mask != 0; mask >>= 1) putchar('0' | !!(n & mask));
putchar('\n');
}


int main() {
uint32_t n = 12;
print_binary(n);
n = reverse_bits(n);
print_binary(n);
unsigned char c = 'a';
print_binary(c);
c = reverse_bits(c);
print_binary(c);
uint16_t s = 12;
print_binary(s);
s = reverse_bits(s);
print_binary(s);
uint64_t l = 12;
print_binary(l);
l = reverse_bits(l);
print_binary(l);
return 0;
}

反转,不稳定

最后但并非最不重要的是,如果最简单意味着更少的行,为什么不尝试内联汇编?

您可以通过在编译时添加 -masm=intel 来测试下面的代码片段:

unsigned char reverse_bits(unsigned char c) {
__asm__ __volatile__ (R"(
mov cx, 8
daloop:
ror di
adc ax, ax
dec cx
jnz short daloop
;)");
}

逐行解释:

        mov cx, 8       ; we will reverse the 8 bits contained in one byte
daloop:             ; while loop
shr di          ; Shift Register `di` (containing value of the first argument of callee function) to the Right
rcl ax          ; Rotate Carry Left: rotate ax left and add the carry from shr di, the carry is equal to 1 if one bit was "lost" from previous operation
dec cl          ; Decrement cx
jnz short daloop; Jump if cx register is Not equal to Zero, else end loop and return value contained in ax register

如果您使用的是小型微控制器,需要高速解决方案,占地面积小,这可能是解决方案。它可以用于 C 项目,但您需要添加这个文件作为汇编程序文件 * 。阿姆斯特朗,你的 C 项目。 说明: 在 C 项目中添加以下声明:

extern uint8_t byte_mirror(uint8_t);

从 C 调用这个函数

byteOutput= byte_mirror(byteInput);

这是代码,它只适用于8051核心。在 CPU 寄存器中,R0是来自 ByteInput的数据。代码向右旋转 R0交叉进位,然后向左旋转进位到 R1。每个位重复这个过程8次。然后寄存器 r1作为 byteOutput 返回给 c 函数。在8051核心只能旋转蓄能器

NAME     BYTE_MIRROR
RSEG     RCODE
PUBLIC   byte_mirror              //8051 core


byte_mirror
mov r3,#8;
loop:
mov a,r0;
rrc a;
mov r0,a;
mov a,r1;
rlc a;
mov r1,a;
djnz r3,loop
mov r0,a
ret

优点: 占地面积小,速度快 反对: 这是不可重用的代码,它只适用于8051

011101101-> 进位

101101110 <-携带

它简单而迅速:

无符号字符反转(无符号字符 rv)
{
无符号字符 tmp = 0;
如果(rv & 0x01) tmp = 0x80;
如果(rv & 0x02) tmp | = 0x40;
如果(rv & 0x04) tmp | = 0x20;
如果(rv & 0x08) tmp | = 0x10;
如果(rv & 0x10) tmp | = 0x08;
如果(rv & 0x20) tmp | = 0x04;
如果(rv & 0x40) tmp | = 0x02;
如果(rv & 0x80) tmp | = 0x01;
返回 tmp;
}

这是一个类似于某事物的优秀答案的方法,但有优化,支持高达64位整数,以及其他一些小的改进。

我利用 C + + 模板函数 reverse_bits()让编译器优化可能传递给函数的各种字长的整数。这个函数应该可以正确地处理任何8位的倍数字,最多可以达到64位。如果编译器支持长度超过64位的单词,则该方法可以直接进行扩展。

这是一个完整的、可以随时编译的示例,其中包含必要的标头。有一个方便的模板函数 to_binary_str()用于创建二进制数的 std: : string 表示形式,以及一些具有不同字大小的调用来演示所有内容。

如果删除注释和空行,该函数将非常紧凑,并且在视觉上令人满意。

您可以在 LabStack给你上试用它。

// this is the only header used by the reverse_bits() function
#include <type_traits>


// these headers are only used by demonstration code
#include <string>
#include <iostream>
#include <cstdint>




template<typename T>
T reverse_bits( T n ) {
// we force the passed-in type to its unsigned equivalent, because C++ may
// perform arithmetic right shift instead of logical right shift, depending
// on the compiler implementation.
typedef typename std::make_unsigned<T>::type unsigned_T;
unsigned_T v = (unsigned_T)n;


// swap every bit with its neighbor
v = ((v & 0xAAAAAAAAAAAAAAAA) >> 1)  | ((v & 0x5555555555555555) << 1);


// swap every pair of bits
v = ((v & 0xCCCCCCCCCCCCCCCC) >> 2)  | ((v & 0x3333333333333333) << 2);


// swap every nybble
v = ((v & 0xF0F0F0F0F0F0F0F0) >> 4)  | ((v & 0x0F0F0F0F0F0F0F0F) << 4);
// bail out if we've covered the word size already
if( sizeof(T) == 1 ) return v;


// swap every byte
v = ((v & 0xFF00FF00FF00FF00) >> 8)  | ((v & 0x00FF00FF00FF00FF) << 8);
if( sizeof(T) == 2 ) return v;


// etc...
v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
if( sizeof(T) <= 4 ) return v;


v = ((v & 0xFFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF) << 32);


// explictly cast back to the original type just to be pedantic
return (T)v;
}




template<typename T>
std::string to_binary_str( T n ) {
const unsigned int bit_count = sizeof(T)*8;
char s[bit_count+1];
typedef typename std::make_unsigned<T>::type unsigned_T;
unsigned_T v = (unsigned_T)n;
for( int i = bit_count - 1; i >= 0; --i ) {
if( v & 1 )
s[i] = '1';
else
s[i] = '0';


v >>= 1;
}
s[bit_count] = 0;  // string null terminator
return s;
}




int main() {
{
char x = 0xBA;
std::cout << to_binary_str( x ) << std::endl;


char y = reverse_bits( x );
std::cout << to_binary_str( y ) << std::endl;
}
{
short x = 0xAB94;
std::cout << to_binary_str( x ) << std::endl;


short y = reverse_bits( x );
std::cout << to_binary_str( y ) << std::endl;
}
{
uint64_t x = 0xFEDCBA9876543210;
std::cout << to_binary_str( x ) << std::endl;


uint64_t y = reverse_bits( x );
std::cout << to_binary_str( y ) << std::endl;
}
return 0;
}

在各种网上资源的帮助下,我为自己草草记下了这些(不确定它们是否100% 准确) :

#                 octal       hex


# bit-orig    : 01234567    01234567:89ABCDEF
# bit-invert  : 76543210    FEDCBA98:76543210
#
# clz         : 32110000    43221111:00000000
# clo/ffs     : 00001123    00000000:11112234

位反向: [1 [2 [38 [4 C [5 A [6 E [79 [8 D [9 B 00 F 01

# cto         : 01020103    01020103:01020104
# ctz         : 30102010    40102010:30102010

但是,只有当您的输入已经是十六进制或八进制时,这才是最方便的。

在这两种格式(8或16)中,您将注意到在位反射之后,所有偶数索引都位于前半部分。我还在十六进制边上高亮显示了相同的0-7,以帮助可视化它。

事实上,人们甚至不需要做一个双子字符串。查找字符串可以用于查找所需的字母,也可以简单地将其用作索引查找。这就是我自己反映 CRC32多项式的方法:

(z is the input polynomial (or just any hex string)


xn = 0 ^ (x = length(z));          # initialize to numeric 0,
# foo^bar in awk means
# foo-to-bar-th-power.
# same as foo**bar in other langs


y = substr(_REF_bitREV_hex, 2);   # by pre-trimming the lookup str,
# it allows skipping the + 1 at
# every cycle of the loop
do {
xn *= 16
xn += index(y, substr(z,x,1)) # keep in mind that this is awk syntax,
# where strings start at index-1, not zero.
} while ( 1 < x—- );

使用基于十六进制或八进制的方法的一个优点是,它允许任意长度的输入,支持任意精度的操作,而不必使用适当的 BigInteger 或 BigFloat 库。为此,您必须为新的数字/字母添加子字符串,并执行字符串 concats,而不是每次都简单地添加。

关于所有256字节的位反射查找表,只需几个循环,就可以非常快速地从头开始生成它(从十六进制到字节的映射应该很简单) :

 #  gawk profile,created Tue Jul 26 22:22:18 2022


#  BEGIN rule(s)
BEGIN {
1      print initREF()
}
# Functions,listed alphabetically
1  function initREF(_,__,___,____,_____,______,_______)
{
1     ______=(_+=_^=_<_)^++_-(_=\
__=(+(___=____="."))(_~_))


1     gsub(___,"&\\&",_)
1     _____[_<_]
_____[ +_]


7     do {
7         gsub(___,_,__)
7         ___=___""____
} while (—______)


1     gsub("....","=&", __)
1     _+=_^=_<_;_______=__;


2     for(______ in _____) { ______*=_*_*_
4        for(____ in _____) {  ____*=_+_
8           for(___ in _____) { ___*= +_
16              for(__ in _____) {


16                    gsub("=" (_<______)(_<____) (_~___)__,
sprintf("%X", __+___+____+______),_______)
} } } }


1      __=_______
1       _="[^ ]+[ ]"
1      gsub(".",_,_)


1      gsub("..","0x&, ",__)
1      gsub((_)_,  "&\n",__)
1      sub("[\1-@]+$","",__)
1            gsub(" ","",__)


1      return __
}

|

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 char   reverse_bits(unsigned char octet)
{
return  (((octet >> 0) & 1) << 7) | \
(((octet >> 1) & 1) << 6) | \
(((octet >> 2) & 1) << 5) | \
(((octet >> 3) & 1) << 4) | \
(((octet >> 4) & 1) << 3) | \
(((octet >> 5) & 1) << 2) | \
(((octet >> 6) & 1) << 1) | \
(((octet >> 7) & 1) << 0);
}