Оптимизировать проверку битового флага

Как я могу оптимизировать следующий код?

   ( (kbd_flags & KBD_FLAG_SHIFT) && !(kbd_flags & KBD_FLAG_CAPS))
|| (!(kbd_flags & KBD_FLAG_SHIFT) &&  (kbd_flags & KBD_FLAG_CAPS))

В основном, я хочу проверить, установлен ли KBD_FLAG_SHIFT или KBD_FLAG_CAPS, но не оба.

3 ответа

Решение

Я хотел бы поделиться своим решением после проведения некоторых исследований. Выражение Зодоха можно упростить до

kbd_flags & KBD_FLAG_SHIFT
    ? !(kbd_flags & KBD_FLAG_CAPS)
    :   kbd_flags & KBD_FLAG_CAPS

(Опуская ненужные скобки и true а также false выражения)

Еще один интересный способ, который я понял, заключается в следующем:

x = kbd_flags & (KBD_FLAG_SHIFT | KBD_FLAG_CAPS);
return x && (x & x - 1) == 0;

Это работает из-за способа обозначения дополнения двух. В качестве примера, если kbd_flags установлен в 001000, x - 1 будет 000111 а также 001000 & 000111 является 000000, В следствии, 000000 равно 0возвращая таким образом true, Первый x выражение гарантирует, что регистр "no bit set" исключен.

Он также будет работать не только с двумя битовыми флагами:

#define A 0x01
#define B 0x02
#define C 0x04
#define D 0x08
#define E 0x10
#define F 0x20

x = flags & (A | B | C | D | E | F);
return x && (x & x - 1) == 0;

Здесь выражение x && (x & x - 1) == 0 будет true если и только если один из флагов A через F установлено.

Быстрый тест (f целое число, содержащее флаги для проверки):

int f, x;
for (f = 0; f <= F + 1; f++) {
    x = f & (A | B | C | D | E | F);
    printf("0x%02x %d%d%d%d%d%d -> %d\n", f
        , (f & A) == A, (f & B) == B, (f & C) == C
        , (f & D) == D, (f & E) == E, (f & F) == F
        , x && (x & x - 1) == 0);
}

Этот код выведет следующее:

0x00 000000 -> 0
0x01 100000 -> 1
0x02 010000 -> 1
0x03 110000 -> 0
0x04 001000 -> 1
0x05 101000 -> 0
0x06 011000 -> 0
0x07 111000 -> 0
0x08 000100 -> 1
0x09 100100 -> 0
0x0a 010100 -> 0
0x0b 110100 -> 0
0x0c 001100 -> 0
0x0d 101100 -> 0
0x0e 011100 -> 0
0x0f 111100 -> 0
0x10 000010 -> 1
0x11 100010 -> 0
0x12 010010 -> 0
0x13 110010 -> 0
0x14 001010 -> 0
0x15 101010 -> 0
0x16 011010 -> 0
0x17 111010 -> 0
0x18 000110 -> 0
0x19 100110 -> 0
0x1a 010110 -> 0
0x1b 110110 -> 0
0x1c 001110 -> 0
0x1d 101110 -> 0
0x1e 011110 -> 0
0x1f 111110 -> 0
0x20 000001 -> 1
0x21 100001 -> 0

Как вы видете, x && (x & x - 1) == 0 является true если один бит установлен.

Чтобы ответить более четко.

(kbd_flags & KBD_FLAG_SHIFT) ^ (kbd_flags & KBD_FLAG_CAPS)

Это должно работать.

Вы можете использовать это

(kbd_flags & KBD_FLAG_SHIFT) ? ((kbd_flags & KBD_FLAG_CAPS)? false : true) : ((kbd_flags & KBD_FLAG_CAPS)? true : false)

Еще вы знаете, как использовать биты, которые вы можете использовать оператор XOR ^.

A = 0000 0001
B = 0000 0000
A ^ B = 0000 0001

A = 0000 0000
B = 0000 0001
A ^ B = 0000 0001

A = 0000 0000
B = 0000 0000
A ^ B = 0000 0000

A = 0000 0001
B = 0000 0001
A ^ B = 0000 0000
Другие вопросы по тегам