В C/C++ каков самый простой способ изменить порядок битов в байте?
Хотя существует несколько способов изменить порядок битов в байте, мне любопытно, что является "самым простым"для разработчика. И под обращением я имею в виду:
1110 -> 0111
0010 -> 0100
это похоже, но не дубликат этого вопроса PHP.
это похоже, но не дубликат этого вопроса C. Этот вопрос задает самый простой метод для реализации разработчиком. "Лучший алгоритм" связан с производительностью памяти и процессора.
25 ответов:
Если вы говорите об одном байте, поиск таблицы, вероятно, является лучшим выбором, если по какой-то причине у вас нет 256 байтов.
Это должно работать:
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; }сначала левые четыре бита меняются местами с правыми четырьмя битами. Затем все соседние пары меняются местами, а затем все соседние одиночные биты. Это приводит к обратному порядку.
Я думаю, что таблица подстановки одним из самых простых методов. Однако вам не нужна полная таблица поиска.
//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 дешев, и таблица легко помещается на строку кэша.
посмотреть бит сложа хаки для многих решений. Copypasting оттуда, очевидно, прост в реализации. =)
например (на 32-битном процессоре):
uint8_t b = byte_to_reverse; b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;Если под "простой реализацией" подразумевается то, что можно сделать без ссылки на экзамен или собеседование, то самой безопасной ставкой, вероятно, является неэффективное копирование битов один за другим в другую переменную в обратном порядке (уже показано в других ответах).
поскольку никто не опубликовал полное решение для поиска таблицы, вот мое:
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]; }
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; }EDIT:
преобразовал его в шаблон с дополнительным bitcount
две строки:
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);"оригинал" - это байт, который вы хотите изменить. "reversed" - это результат, инициализированный до 0.
хотя, вероятно, не портативный, я бы использовал язык ассемблера.
Многие языки ассемблера имеют инструкции для поворота бита в флаг переноса и поворота флага переноса в слово (или байт).алгоритм:
for each bit in the data type: rotate bit into carry flag rotate carry flag into destination. end-forкод языка высокого уровня для этого намного сложнее, потому что C и C++ не поддерживают вращение для переноса и вращение от переноса. Флаг переноса должен быть смоделирован.
Edit: сборка например, язык
; 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.
на простой способ, вероятно, перебирать битовые позиции в цикле:
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; }
Я нахожу следующее решение проще, чем другие алгоритмы битовой скрипки, которые я видел здесь.
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
на постоянная, 8-бит вход, это не стоит памяти или процессора во время выполнения:
#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) метки противоположен остальной части слова. Метка часто бывает постоянной,причем условно в восьмеричной. Например:
#define LABEL_HF_COMM MSB2LSB(0205)
вы можете быть заинтересованы в
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; }остальные варианты могут быть быстрее.
EDIT: я должен вам решение с помощью
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) заключается в том, что вы не ограничены байтами или словами, но можете отменить произвольное количество битов.HTH
таблица поиска или
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; }edit
посмотреть здесь для других решений, которые могут работать лучше для вас
перед реализацией любого алгоритмического решения проверьте язык ассемблера для любой используемой архитектуры процессора. Ваша архитектура может включать инструкции, которые обрабатывают побитовые манипуляции, подобные этому (и что может быть проще, чем одна инструкция по сборке?).
Если такая инструкция недоступна, то я бы предложил перейти с маршрутом таблицы поиска. Вы можете написать сценарий / программу для создания таблицы для вас, и операции поиска будут быстрее, чем любой из алгоритмов реверсирования битов здесь (за счет необходимости хранить таблицу поиска где-то).
более медленная, но более простая реализация:
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; }
может ли это быть быстрым решением?
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! но эксперты, пожалуйста, скажите мне, если это эффективно и быстрее?
эта простая функция использует маску для проверки каждого бита во входном байте и передачи его в сдвигающий выход:
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; }
этот основан на одном 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 )
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; }
#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; }
#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; }
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 зарегистрироваться
Это старый вопрос, но никто, кажется, не показал ясный простой способ (ближайший был edW). Я использовал C# (и замечательный LinqPad чтобы проверить это), но в этом примере нет ничего, что нельзя было бы легко сделать в C.
вставьте следующее В замечательный LinqPad (https://www.linqpad.net/), (Вот где я проверил это):
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); }
Comments