Как установить (самым элегантным образом) ровно n
младшие значащие бит uint32_t
? То есть написать функцию void setbits(uint32_t *x, int n);
. Функция должна обрабатывать каждый n
от 0
до 32
.
Следует учитывать значение n==32
.
Как установить (самым элегантным образом) ровно n
младшие значащие бит uint32_t
? То есть написать функцию void setbits(uint32_t *x, int n);
. Функция должна обрабатывать каждый n
от 0
до 32
.
Следует учитывать значение n==32
.
Если вы имели в виду наименее значимые n бит:
((uint32_t)1 << n) - 1
В большинстве архитектур это не будет работать, если n равно 32, поэтому вам может понадобиться сделать специальный случай:
n == 32 ? 0xffffffff : (1 << n) - 1
В 64-битной архитектуре (возможно) более быстрое решение заключается в том, чтобы запуститься вниз:
(uint32_t)(((uint64_t)1 << n) - 1)
Фактически, это может быть даже быстрее в 32-битной архитектуре, поскольку оно позволяет избежать ветвления.
Вот метод, который не требует никакой арифметики:
~(~0u << n)
Другие ответы не обрабатывают специальный случай n == 32
(смещение на большее или равное ширине типа UB), так что здесь лучший ответ:
(uint32_t)(((uint64_t)1 << n) - 1)
В качестве альтернативы:
(n == 32) ? 0xFFFFFFFF : (((uint32_t)1 << n) - 1)
const uint32_t masks[33] = {0x0, 0x1, 0x3, 0x7 ...
void setbits(uint32_t *x, int n)
{
*x |= masks[n];
}
Если вы имеете в виду самые значительные n бит:
-1 ^ ((1 << (32 - n)) - 1)
Если n равно нулю, ни один бит не должен устанавливаться на основе вопроса.
const uint32_t masks[32] = {0x1, 0x3, 0x7, ..., 0xFFFFFFFF};
void setbits(uint32_t *x, int n)
{
if ( (n > 0) && (n <= 32) )
{
*x |= masks[--n];
}
}
Цель:
void setbits(uint32_t *x, unsigned n) {
// As @underscore_d notes in the comments, this line is
// produces Undefined Behavior for values of n greater than
// 31(?). I'm ok with that, but if you're code needs to be
// 100% defined or you're using some niche, little-used
// compiler (perhaps for a microprocesser?), you should
// use `if` statements. In fact, this code was just an
// an experiment to see if we could do this in only 32-bits
// and without any `if`s.
*x |= (uint32_t(1) << n) - 1;
// For any n >= 32, set all bits. n must be unsigned
*x |= -uint32_t(n>=32);
}
Примечание: если вам нужно n
иметь тип int
, добавьте это в конец:
// For any n<=0, clear all bits
*x &= -uint32_t(n>0);
Пояснение:
*x |= -uint32_t(n>=32);
Когда n>=32
истинно, x
будет побитовым ORED с 0xFFFFFFFF, получив x
со всеми установленными битами.
*x &= -uint32_t(n>0);
В этой строке указано, что до тех пор, пока любой бит должен быть установлен, n>0
, побитовое-AND x
с 0xFFFFFFFF, которое не приведет к изменению на x
. Если n<=0
, x
будет побитовым-ANDed с 0 и, следовательно, приведет к значению 0.
Пример программы для работы алгоритма:
#include <stdio.h>
#include <stdint.h>
void print_hex(int32_t n) {
uint32_t x = (uint32_t(1) << n);
printf("%3d: %08x |%08x |%08x &%08x\n",
n, x, x - 1,
-uint32_t(n>=32),
-uint32_t(n>0));
}
void print_header() {
// 1: 00000002 |00000001 |00000000 &ffffffff
printf(" n: 1 << n (1<<n)-1 n >= 32 n <= 0\n");
}
void print_line() {
printf("---------------------------------------------\n");
}
int main() {
print_header();
print_line();
for (int i=-2; i<35; i++) {
print_hex(i);
if (i == 0 || i == 31) {
print_line();
}
}
return 0;
}
Выход (разбит и аннотирован):
Для n < = 0
, последний шаг AND с 0, обеспечивающий результат, равен 0.
n: 1 << n (1<<n)-1 n >= 32 n <= 0
---------------------------------------------
-2: 40000000 |3fffffff |00000000 &00000000
-1: 80000000 |7fffffff |00000000 &00000000
0: 00000001 |00000000 |00000000 &00000000
Для 1 <= n <= 31
последние два шага "OR 0, AND 0xffffffff" не приводят к изменению номера. Единственным важным шагом является "OR (1 <
n: 1 << n (1<<n)-1 n >= 32 n <= 0
---------------------------------------------
1: 00000002 |00000001 |00000000 &ffffffff
2: 00000004 |00000003 |00000000 &ffffffff
3: 00000008 |00000007 |00000000 &ffffffff
4: 00000010 |0000000f |00000000 &ffffffff
5: 00000020 |0000001f |00000000 &ffffffff
6: 00000040 |0000003f |00000000 &ffffffff
7: 00000080 |0000007f |00000000 &ffffffff
8: 00000100 |000000ff |00000000 &ffffffff
9: 00000200 |000001ff |00000000 &ffffffff
10: 00000400 |000003ff |00000000 &ffffffff
11: 00000800 |000007ff |00000000 &ffffffff
12: 00001000 |00000fff |00000000 &ffffffff
13: 00002000 |00001fff |00000000 &ffffffff
14: 00004000 |00003fff |00000000 &ffffffff
15: 00008000 |00007fff |00000000 &ffffffff
16: 00010000 |0000ffff |00000000 &ffffffff
17: 00020000 |0001ffff |00000000 &ffffffff
18: 00040000 |0003ffff |00000000 &ffffffff
19: 00080000 |0007ffff |00000000 &ffffffff
20: 00100000 |000fffff |00000000 &ffffffff
21: 00200000 |001fffff |00000000 &ffffffff
22: 00400000 |003fffff |00000000 &ffffffff
23: 00800000 |007fffff |00000000 &ffffffff
24: 01000000 |00ffffff |00000000 &ffffffff
25: 02000000 |01ffffff |00000000 &ffffffff
26: 04000000 |03ffffff |00000000 &ffffffff
27: 08000000 |07ffffff |00000000 &ffffffff
28: 10000000 |0fffffff |00000000 &ffffffff
29: 20000000 |1fffffff |00000000 &ffffffff
30: 40000000 |3fffffff |00000000 &ffffffff
31: 80000000 |7fffffff |00000000 &ffffffff
Для n >= 32
все биты должны быть установлены, и шаг "OR ffffffff" выполнит это, независимо от того, что мог сделать предыдущий шаг. Шаг n <= 0
также является noop с AND ffffffff
.
n: 1 << n (1<<n)-1 n >= 32 n <= 0
---------------------------------------------
32: 00000001 |00000000 |ffffffff &ffffffff
33: 00000002 |00000001 |ffffffff &ffffffff
34: 00000004 |00000003 |ffffffff &ffffffff
((((1 << (n - 1)) - 1) << 1) | 1)
Установите последние n бит. n должно быть> 0. Работа с n = 32.
Функция с простым тестом:
#include <stdio.h>
#include <stdint.h>
void setbits(uint32_t *x, int n)
{
*x |= 0xFFFFFFFF >> (32 - n);
}
int main()
{
for (int n = 1; n <= 32; ++n)
{
uint32_t x = 0;
setbits(&x, n);
printf("%2d: 0x%08X\n", n, x);
}
getchar();
return 0;
}