View Single Post
  #1  
Old 11-15-2006, 09:25 AM
jake13jake jake13jake is offline
Former Classic Staff
jake13jake's Avatar
Join Date: Dec 2002
Location: Northwest Vermont
Posts: 1,452
jake13jake will become famous soon enough
Bitflag Tutorial

Why use a bitflag?
  • They require less memory.
  • Bitwise operations take much less execution time than other operations.
  • With the small amount of space they require, you can greatly reduce server traffic.

What is a bitflag? A bitflag is an integer that represents multiple cases of true and false.

In order to understand this, you might take a look at an integer in binary. First of all, how do you read a binary value?

0000 is 0
0001 is 1
0010 is 2
0011 is 2+1
0100 is 4
0101 is 4+1
0111 is 4+2+1

Do you see a pattern of the powers of two emerging? If you don't, I suggest you go get a catscan.

Each value in a binary number on computers is a bit. The bit is either on (1) or off (0).

Amazingly, we have operators to manipulate numbers at the bit level.
& (bitwise and), &= (bitwise and assignment)
| (bitwise or), |= (bitwise or assignment)
xor (bitwise exclusive or), unfortunately gscript lacks xor assignment
<< (left bitshift), <<= (left bitshift assignment)
>> (signed right bitshift), >>= (signed right bitshift assignment)
unfortunately, gscript lacks unsigned right bitshift/assignment.

What can we do with these operators

Let's look at different cases (pretending we're working in 8 bits):

~4
00000100 (4 in binary)
11111011 (~4)
The inversion operator makes all the 0s into 1s and all the 1s into 0s.

4 & 5
00000100 (4 in binary)
00000101 (5 in binary)
00000100 (4 & 5)
If the bits are both on, then the bit in the outcome is on.

4 | 5
00000100
00000101
00000101 (4 | 5)
If either of the bits are on, then the bit in the outcome is on.

4 xor 5
00000100
00000101
00000001 (4 xor 5)
If the bit is one or the other but not both, the bit is on in the outcome.

4 << 1
00000100
00001000 (4 << 1)
What does this do? It shifts all the bits left one and fills in with 0. This is also identical to multiplying by a power of 2, the right operand being the power.

4 >> 1
00000100
00000010 (4 >> 1)
This shifts all the bits right one, and fills in the last bit with a copy of the previous last bit (which in this case would be 0). Other than the last bit, this would be identical to integer division by a power of 2.

This is because the last bit is the SIGN bit.
11111111 would be -1.
11111110 would be -2, and so on.
If you right shifted either of these examples by more than one, the outcome would be -1. If you wanted to fill in the last bit with a 0, you'd need to use an unsigned bit, which gscript lacks.

PHP Code:
//Okay, let's say you want to make constants for a bit flag.
INFECTED = (<< 0); //player has a virus (1)
IMMUNE = (<< 1); //player is immune to the virus (2)
SICK = (<< 2); //player is sick due to the virus (4)
FATAL = (<< 3); //the virus is fatal to the player (8)
//and so on...

//This makes it so that each constant represents a particular bit. 
//You could also make a constant represent all of the bits. 
ALLBITS = -1;

//So now that we have our constants sorted out, we will initialize a bitflag.
player.clientr.flag 0;

//how do we put different values in the flag?
player.clientr.flag |= INFECTED//(sets a bit, or leaves it set)
player.clientr.flag &= ~INFECTED//(unsets a bit or leaves it unset)
player.clientr.flag player.clientr.flag xor INFECTED//(using the xor operator will toggle the bit)

//The player should be infected. 
//How do we check to see if the player is infected?
if (player.clientr.flag INFECTED == INFECTED)
  
doInfectionStuff();

//You can also check for multiple bits at once.
temp.mask INFECTED SICK;
if (
player.clientr.flag temp.mask == temp.mask)
  
doMaskStuff();

//What if you just need one of the bits your checking in the mask to be true?
if (player.clientr.flag temp.maks != 0)
  
doOtherStuff();

//What if you want to know what the first set or first unset bit are?
this.firstset & (~1); // can also be written c & -c;
this.firstunset = ~& (1); 
That pretty much covers the whole concept. Also, keep in mind that you have 32 whole bits you can use for one flag in gscript, not just 4 or 8.

Here are some other neat tricks that you should only use for integers.

var * 2^n;
is equivalent to
var << n;

var / 2^n;
is equivalent to
var >> n; (integer division, 3>>1 returns 1, not 1.5)


var % (2^n);
is equivalent to
var & (2^n - 1);

-var is equivalent to (~var + 1)... though this isn't so useful.

var = var xor var;
is equivalent to and for some computers even faster than
var = 0;

Here's a useful one though. Swapping two numbers without a temp value.
PHP Code:
if (var1 != var2) {
  
var1 var1 xor var2;
  
var2 var2 xor var1;
  
var1 var1 xor var2;

The body without the if branch would work for all values, except for when you're trying to swap a number with itself. This would mean, in essence that var1 is the same variable as var2. The example of this that is most often encountered is by swapping arr[i] and arr[j], when i==j.

I hope you join me in my protest for an unsigned bitshift operator (>>>) and a xor assignment operator, and that you can all use bitflags after reading this.
Reply With Quote