Если вы хотите строить его каждый раз вместе с вашим проектом, самым простым способом было бы:
Однако это редко требуется, и в большинстве случаев вам нужно просто построить библиотеку один раз и связать ее как с любой другой внешней библиотекой.
Мне нравятся оба решения. Вот еще один способ, который приходит мне на ум (возможно, не лучше).
((~((unsigned int)0) << k) >> (k + n)) << n
ИЗМЕНИТЬ: В моей предыдущей версии произошла ошибка (это было без неподписанного int cast ). Проблема заключалась в том, что ~0 >> n
добавляет 1s спереди, а не 0s.
И да, этот подход имеет один большой недостаток; он предполагает, что вы знаете количество бит целочисленного типа по умолчанию или, другими словами, оно предполагает, что вы действительно знаете k, тогда как другие решения не зависят от k. Это делает мою версию менее переносной или, по крайней мере, труднее переносить. (Он также использует 3 смены и добавление и оператор побитового отрицания, что является двумя дополнительными операциями.)
Таким образом, вам лучше использовать один из других примеров.
Здесь это небольшое тестовое приложение, сделанное Джонатаном Леффлером, для сравнения и проверки вывода различных решений:
#include <stdio.h>
#include <limits.h>
enum { ULONG_BITS = (sizeof(unsigned long) * CHAR_BIT) };
static unsigned long set_mask_1(int k, int m, int n)
{
return ~(~0 << m) << n;
}
static unsigned long set_mask_2(int k, int m, int n)
{
return ((1 << m) - 1) << n;
}
static unsigned long set_mask_3(int k, int m, int n)
{
return ((~((unsigned long)0) << k) >> (k + n)) << n;
}
static int test_cases[][2] =
{
{ 1, 0 },
{ 1, 1 },
{ 1, 2 },
{ 1, 3 },
{ 2, 1 },
{ 2, 2 },
{ 2, 3 },
{ 3, 4 },
{ 3, 5 },
};
int main(void)
{
size_t i;
for (i = 0; i < 9; i++)
{
int m = test_cases[i][0];
int n = test_cases[i][1];
int k = ULONG_BITS - (m + n);
printf("%d/%d/%d = 0x%08lX = 0x%08lX = 0x%08lX\n", k, m, n,
set_mask_1(k, m, n),
set_mask_2(k, m, n),
set_mask_3(k, m, n));
}
return 0;
}
В то время как верхние ответы просты и эффективны, они не устанавливают MSB для случая, когда n=0
и m=31
:
~(~0 << 31) << 0
= 0111 1111 1111 1111 1111 1111 1111 1111
((1 << 31)-1) << 0
= 0111 1111 1111 1111 1111 1111 1111 1111
Мое предложение для 32-битного слова без знака (которое является уродливым и имеет ветвь) выглядит следующим образом:
unsigned int create_mask(unsigned int n,unsigned int m) {
// 0 <= start_bit, end_bit <= 31
return (m - n == 31 ? 0xFFFFFFFF : ((1 << (m-n)+1)-1) << n);
}
на самом деле получает бит в диапазоне [m,n]
(закрытый интервал), поэтому create_mask(0,0)
вернет маску для первого бита (бит 0) и create_mask(4,6)
возвращает маску для битов с 4 по 6, т.е. ... 00111 0000
.
(только) Для тех, кто заинтересован в немного более эффективном решении для систем x86 с поддержкой BMI2 (Intel Haswell или новее, AMD Excavator или новее):
mask = _bzhi_u32(-1,m)<<n;
Инструкция bzhi
нули старших бит, начиная с заданной позиции бита. Собственные компиляции _bzhi_u32
этой инструкции. Тестовый код:
#include <stdio.h>
#include <x86intrin.h>
/* gcc -O3 -Wall -m64 -march=haswell bitmsk_mn.c */
unsigned int bitmsk(unsigned int m, unsigned int n)
{
return _bzhi_u32(-1,m)<<n;
}
int main() {
int k = bitmsk(7,13);
printf("k= %08X\n",k);
return 0;
}
Выход:
$./a.out
k= 000FE000
Кодовый фрагмент _bzhi_u32(-1,m)<<n
компилируется в три команды
movl $-1, %edx
bzhi %edi, %edx, %edi
shlx %esi, %edi, %eax
Который является одной инструкцией меньше чем коды @Jonathan Leffler и @Darius Bacon . На процессорах Intel Haswell или новее оба bzhi
и shlx
имеют латентность 1 цикл и пропускную способность 2 за цикл. На AMD Ryzen эти две инструкции имеют пропускную способность 4 за цикл.
~(~0 << m)
находится в пункте 2.9 «Побитовые операторы». «Язык программирования C, второе издание» Брайана Кернигана и Денниса Ричи. Это также в пункте 7.5 «Эффективность пространства». «Практика программирования» Брайана У. Кернигана и Роб Пайка. – Alessandro Jacopson 22 July 2011 в 07:57integer overflow in preprocessor expression
. – Barry 8 October 2015 в 02:35