Search code examples
cbinaryflagsbitflags

Optimize bitflag check


How can I optimize the following code?

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

Basically, I want to check if either KBD_FLAG_SHIFT or KBD_FLAG_CAPS is set, but not both.


Solution

  • I would like to share my solution after doing some research. Zodoh's expression can be simplified to

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

    (Omitting unneeded parentheses and the true and false expressions)

    Another interesting way I figured out is the following:

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

    This works because of the way two's complement notation is designed. As an example, if kbd_flags is set to 001000, x - 1 will be 000111 and 001000 & 000111 is 000000. As a result, 000000 is equal to 0, thus returning true. The first x expression makes sure that the "no bit set" case is excluded.

    It will also work with more than only two bit flags:

    #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;
    

    Here, the expression x && (x & x - 1) == 0 will be true if and only if one of the flags A through F is set.

    A quick test (f being the integer holding the flags to test for):

    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);
    }
    

    This code will output the follwing:

    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
    

    As you can see, x && (x & x - 1) == 0 is true iff one bit is set.