QuakeC bit operations

VoidForce

New member
Mar 15, 2022
2
5
3
34
Barbarian Land
Hello, world! This small article can be used as a simple guide to basic operations with bits in QuakeC.

First we need to define some constants, so we can operate with names and not numbers.
Defenition of constants, usually goes to defs.qc, in copper it's constants.qc
C:
float BIT_NULL    = 0;
float BIT_FLY     = 1; //smallest bit possible
float BIT_CAT     = 2;
float BIT_DOG     = 4;
float BIT_DRAGON  = 8;
float BIT_MOUSE   = 16;
float BIT_OGRE    = 32;
float BIT_SNAKE   = 64;
float BIT_MONKEY  = 128;

Bits that are available for float type:
C:
    1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096,
    8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576,
    2097152, 4194304, 8388608  //largest bit possible, before lowest bits get eaten by float imprecision (thanks bmFbr)

Below we would refer to left side parameter as flags, and right side parameter as mask (bitmask) we would store our bits in the flags variable

Operators below is valid for FTEQCC compiler!

Operation bitwise NOT uses ~ symbol.
Sets any bit that was clear, and clears any bit that was set.

Operation bitwise OR uses | symbol.
If any bit from the mask is not presented in the flags, it will be added to the flags.
Code:
    A | B | C = A + B + C
    A | (A + B) = A + B
Operation XOR uses ^ symbol. A ^ B = { return (A | B) - (A & B); }
In simple words it's useful for toggling individual bits:
Code:
    A ^ (A | B | C) = B + C
    A ^ (B | C) = A + B + C
Operation bitwise AND uses & symbol.
It returns only matching bits.
Code:
    A & (A | B | C) = A
    (A | C) & (A | B | C) = A + C

Here comes examples in qc code:

C-like:
.float flags; // will store our bits in this field
entity edict; // any game's entity that will hold our .flags field
local float A,B,C,D;

// addition of a bit to the flags

    edict.flags = BIT_FLY; // set to BIT_FLY
    edict.flags = edict.flags | BIT_DRAGON; // add dragon bit = 1 + 8 : fly and dragon
    edict.flags |= BIT_DRAGON; // simplified form of the line above
   
    // be very careful with regular sum operation instead of bitwise OR, because it will cause a big problems
    // for example if we add BIT_MOUSE one more time it will turn into OGRE (16 + 16 = 32), so we have logic error
    A  = BIT_DOG + BIT_CAT + BIT_MOUSE; // 4 + 2 + 16 = 22 DOG, CAT, MOUSE
    A += BIT_MOUSE; // (4 + 2 + 16) + 16 = 4 + 2 + 32, DOG, CAT and OGRE

    // instead use bitwise OR operator:
    A = BIT_DOG | BIT CAT | BIT_MOUSE; // A will contain DOG, CAT and MOUSE (4 + 2 + 16 = 22)
    A |= BIT_MOUSE; // it still be (22): DOG, CAT, MOUSE
    A |= BIT_NULL;  // A would not be changed, A | 0 = A

    edict.flags = edict.flags | A; // bits = 1 + 8 + (2 + 4 + 16) = 31; BIT_FLY, DRAGON, DOG, CAT and MOUSE

// compare and selecting similar bits

    B = BIT_CAT | BIT_DOG | BIT_MOUSE ; // 2 + 4 + 16
    C = BIT_CAT | BIT_DOG | BIT_OGRE | BIT_SNAKE; // 2 + 4 + 32 + 64
    D = B & C; // (2 + 4 + 16) & (2 + 4 + 32 + 64) = 2 + 4: CAT and DOG
   
// removing of a bit from the flags

    edict.flags = BIT_FLY | BIT_CAT | BIT_DOG | BIT_DRAGON | BIT_MOUSE;
   
    // to remove a bit, first we need to find out if they are presented in the flags:
    B = edict.flags & BIT_DOG;
    //B = (( 1 + 2 + 4 + 8 + 16) & 4) = 4: dog is in the flags
    // then negate them from the flags:
    edict.flags = edict.flags - B;
    //(1 + 2 + 4 + 8 + 16) - 4
    // 1 + 2 + 8 + 16: now there is no dog
   
    // everything at once:
    edict.flags = edict.flags - (edict.flags & BIT_DOG);
    edict.flags -= edict.flags & BIT_DOG; // simplified form of the line above

    //we can negate more bits at once by expanding our bitmask:
    edict.flags = edict.flags - (edict.flags & (BIT_CAT | BIT_DOG | BIT_MONKEY));
    // (1 + 2 + 4 + 8 + 16) - ((1 + 2 + 4 + 8 + 16) & (2 + 4 + 128))
    // (1 + 2 + 4 + 8 + 16) - (2 + 4)
    // 1 + 8 + 16
   
// we can make a logic checks with specified bits individually

    edict.flags = BIT_CAT | BIT_DOG | BIT_MOUSE;
    if (edict.flags & BIT_MONKEY) // false
    if (edict.flags & BIT_CAT) // true
    if ( (edict.flags & BIT_CAT) && (!(edict.flags & BIT_MONKEY)) ) // true