#cpp #c #алгоритм #битовые_операции
Допустим, есть байт 0b00001000, для удобства разделю: 0 b 0000 - 1000 Как заполнить область в байте своим числом? Вот пример, с 1 по 4 бит надо записать 0011, выйдет: 0 b 0000 - 0110 Или с 0 по 1 надо записать 11, выйдет: 0 b 0000 - 1011
Ответы
Ответ 1
#include#include int main() { unsigned int from = 3; // начиная с 4-го бита unsigned int to = 6; // по 6 бит unsigned int val = 11; // записать это значение const int BIT_COUNT = sizeof(unsigned int) * CHAR_BIT; std::bitset x(555); // исходное число std::bitset v(val); std::bitset m(0); // битовая маска std::cout << "x = " << x << std::endl; std::cout << "value = " << v << std::endl; m.flip(); m <<= to - from + 1; m.flip(); m <<= from; m.flip(); std::cout << "mask = " << m << std::endl; x = x & m; v <<= from; x = x | v; std::cout << "res = " << x << std::endl; return 0; } Ответ 2
Делаем маску на нужные биты (например для 2345 битов - 11100001) Обнуляем старые биты используя AND по маске, получим 0 на их месте Подготавливаем новые биты (используем смещение вверх для позиционирования) Записываем новые биты используя ORОтвет 3
Например, можно сделать следующим образом: #include#include #include int main() { std::uint8_t b = 0b00001000; std::uint8_t b1 = ( ( ~0u << 4 ) & b ) | 0b0011; std::uint8_t b2 = ( ( ~0u << 2 ) & b ) | 0b0011; std::cout << "b = " << std::hex << ( int )b << std::endl; std::cout << "b1 = " << std::hex << ( int )b1 << std::endl; std::cout << "b2 = " << std::hex << ( int )b2 << std::endl; return 0; } Вывод на консоль: b = 8 b1 = 3 b2 = b Можете написать отдельную функцию, как, например, std::uint8_t replace( std::uint8_t src, std::uint8_t value, size_t bits ) { return ( ( ~0u << bits ) & src ) | ( ~( ~0u << bits ) & value ); } Вот программа с использованием функции #include #include #include std::uint8_t replace( std::uint8_t src, std::uint8_t value, size_t bits ) { return ( ( ~0u << bits ) & src ) | ( ~( ~0u << bits ) & value ); } int main() { std::uint8_t b = 0b00001000; std::uint8_t b1 = replace( b, 0b0011, 4 ); std::uint8_t b2 = replace( b, 0b0011, 2 ); std::cout << "b = " << std::hex << ( int )b << std::endl; std::cout << "b1 = " << std::hex << ( int )b1 << std::endl; std::cout << "b2 = " << std::hex << ( int )b2 << std::endl; return 0; } Вывод на консоль точно такой же, как показано выше: b = 8 b1 = 3 b2 = b Если нужно задавать позицию, то функция может выглядеть следующим образом std::uint8_t replace( std::uint8_t src, std::uint8_t value, size_t n, size_t pos ) { if ( n == 0 ) return src; return ( ( ~( ( 1u << n ) - 1) << pos ) & src ) | ( ~( ~0u << n ) & value ); } Ответ 4
Используйте битовую арифметику. Пусть есть две переменные unsigned char (по 8 бит в каждой, соответственно). | — побитовое 'или': 0b11 | 0b101 = 0b111 & — побитовое 'и': 0b11 & 0b101 = 0b1 ~ — побитовое 'не': ~0b11 = 0b11111100 << — сдвиг влево: 0b101 << 2 = 0b10100 >> — сдвиг вправо: 0b1010 >> 2 = 0b10 Теперь можно манипулировать битами числа. К примеру, для вашего случая: unsigned char x = 0b00001000 x &= 0b11100001 // очистили место для вставки // x = 0b00001000 & 0b11100001 = 0b00000000 x += (0b0011 << 1) // x = 0b00000000 + (0b0011 << 1) = // = 0b00000000 + 0b00110 = // = 0b00000110Ответ 5
Можно предложить несложную реализацию с побитовой обработкой вставки. function masking($number,$mask,$start,$finish){ $result=$number; for($i=$start; $i<=$finish; $i++){ $bit=1<<$i; $result = ($result|$bit) - $bit + ($bit & $mask); } return $result; } $number=0b00001000; $mask=0b00000110; $number_masked = masking($number,$mask,1,4); printf("number=%b mask=%b start=%2d finish=%2d result=%b
", $number, $mask, 1, 4, $number_masked); $mask=0b00000011; $number_masked = masking($number,$mask,0,1); printf("number=%b mask=%b start=%2d finish=%2d result=%b
", $number, $mask, 0, 1, $number_masked); Результаты: number=1000 mask=110 start= 1 finish= 4 result=110 number=1000 mask=11 start= 0 finish= 1 result=1011Ответ 6
Если можно избежать скучного вычисления битов, избегайте его. Пусть за вас считает компилятор. Воспользуйтесь именоваными структурами и битовыми полями. (Да и наверняка структуры данных наподобие тех, которые я привёл, найдутся в документации.) enum class PCLK_root_divider : unsigned char { pll_clki_1 = 0, pll_clki_2 = 1, pll_clki_4 = 2, pll_clki_8 = 3 }; enum class sclk2x_root_divider : unsigned char { pll_clki_1 = 0, pll_clki_2 = 1, pll_clki_4 = 2, pll_clki_8 = 3 }; enum class SCLK_root_divider : unsigned char { pll_clki_1 = 0, pll_clki_2 = 1, pll_clki_4 = 2, pll_clki_8 = 3 }; struct Whatever { SCLK_root_divider _SCLK_root_divider : 2; sclk2x_root_divider _sclk2x_root_divider : 2; PCLK_root_divider _PCLK_root_divider : 2; unsigned char _debug_mode : 2; }; Только дайте полям какие-то более подходящие имена. Важно: не забывайте про big/little endian! Порядок полей может быть противоположным на другой архитектуре. Проверка: http://ideone.com/RfRrk2Ответ 7
Если отметить область вставки единичными битами шаблона ($template), а саму вставку задать в виде байта ($patch), в котором "свои" биты находятся на требуемых позициях, то результат получается в одну строчку: function patching($number, $patch, $template){ return $number & (255-$template) | $patch & $template; } printf("number=%08b patch=%08b template=%08b result=%08b
", $number=0b00001000, $patch=0b00000110, $template=0b00011110, patching($number, $patch, $template)); printf("number=%08b patch=%08b template=%08b result=%08b
", $number=0b00001000, $patch=0b00000011, $template=0b0000011, patching($number, $patch, $template)); Результаты: number=00001000 patch=00000110 template=00011110 result=00000110 number=00001000 patch=00000011 template=00000011 result=00001011 При этом гибкость процедуры повышается, поскольку вставляемые байты не обязаны находиться рядом.
Комментариев нет:
Отправить комментарий