Bitwise Operators

FreeBasic includes a number of operators that manipulate the bits of a number. The following table lists the bitwise operators in order of their precedence. That is, the first row has the highest precedence while lower rows have lower precedence.

Operator Syntax Truth Table Comments
Not (Bitwise negation) B = NOT expr NOT 0 = 1
NOT 1 = 0
Inverts operand bit; turns a 1 into 0 and a 0 into 1.
And (Bitwise conjunction) B = expr AND expr 0 AND 0 = 0
1 AND 0 = 0
0 AND 1 = 0
1 AND 1 = 1
Result bit is 1 only if both operand bits are 1.
Or (Bitwise disjunction) B = expr OR expr 0 OR 0 = 0
1 OR 0 = 1
0 OR 1 = 1
1 OR 1 = 1
Result bit is 1 if either or both operand bits is 1.
Xor (Bitwise exclusion) B = expr XOR expr 0 XOR 0 = 0
1 XOR 0 = 1
0 XOR 1 = 1
1 XOR 1 = 0
Result bit is 1 if operand bits are different.
Eqv (Bitwise equivalence) B = expr EQV expr 0 EQV 0 = 1
1 EQV 0 = 0
0 EQV 1 = 0
1 EQV 1 = 1
Result bit is 1 if both operand bits are 0 or 1.
Imp (Bitwise implication) B = expr IMP expr 0 IMP 0 = 1
1 IMP 0 = 0
0 IMP 1 = 1
1 IMP 1 = 1
Result bit is 0 if first bit is 1 and second bit is 0, otherwise result is 1.

The truth table column indicates the operation on the individual bits. The order of the expressions are not important except for the IMP operator which tests the bits of the second operand using the bits from the first operand.

The Not Operator

The NOT operator inverts the bits, turning a 1 into 0 and a 0 into 1. You can use the Not operator to preform the Tw0's Complement operation.

myByte:      5   Binary: 00000101
NOT myByte: -6   Binary: 11111010
myByte + 1: -5   Binary: 11111011

The AND Operator

The AND operator can be used to test if an individual bit is 1 or 0 by using a mask value to test for the bit position. Since AND will return a 1 only if both bits are 1, testing a bit position using a mask value will indicate if a 1 is in a particular location.

For example, suppose you want to test the third bit position of a number. Using a mask of 4, you can test whether the third bit position is 1.

Testing 3rd bit position (from right) 
myByte: 5      Binary: 101
Mask:   4      Binary: 100
If (myByte AND Mask) = 4 Then 3rd bit is 1 Else 3rd bit is 0

Looking at the binary values you can see how the bits line up and how the AND operator can test for individual bits. 5 in binary has a bit set in the 2^0 (1) and 2^2 (4) position. Setting the mask value to 4 sets bit position 2^2 to 1 and all other bit positions to 0.

The expression (myByte And Mask) will return an integer value that will contain the AND values of the two operands. Since the mask has zeros in every position except for the 2^2 position, all of the other bits will be masked out, that is 0, returning a 4. Since the return value of 4 matches the target value 4, the 3rd bit is 1.

The OR Operator

You can use the OR operator to set multiple values in a single variable. The Windows API uses this technique to set flags for objects such as the styles of a window.

myByte = 2
myByte = myByte OR 4
mask = 2
If (myByte AND Mask) = 2 Then myByte contains 2

As you can see, you can pack multiple values into a single variable. The number of values a variable can contain depends on the size of data type and the range of values. Using the OR-AND combination is a technique that you will find in wide-spread use, especially in third-party libraries, as it provides an efficient way to pass multiple data items using a single variable.

The XOR Operator

One of the more useful aspects of the XOR operator is to flip bits between two states. XORing a value with 0 will return the original value, and XORing with a 1 returns the opposite value.

Suppose that you start with a 1 bit and XOR with a 0 bit. Since the two inputs are different you will get a 1. If the start value is 0 and you XOR with a 0, then both values are the same and you will get a 0. In both cases the output is the same as the input.

If you start with a 1 and XOR with a 1, you will get a 0 since both inputs are the same. If you start with a 0 and XOR with a 1, you will get a 1 since the inputs are different. Here the inputs have been flipped to the opposite values.

You can use this technique with a mask value, XORing once to get a new value, and then XORing again with the same mask to get the original value.

One use of this technique is to display a sprite on the screen using XOR and then erasing the sprite by using another XOR at the same location. The first XOR combines the bits of the background with the sprite bits to display the image. Another XOR in the same location flips the bits back to their original state, once again showing the background and effectively erasing the sprite image.

XOR can also be used to swap the values of two variables.

myInt1 = myInt1 XOR myInt2
myInt2 = myInt1 XOR myInt2
myInt1 = myInt1 XOR myInt2

This will swap the two values in myInt1 and myInt2 without using a third temporary variable to hold the intermediate result.

The EQV Operator

The EQV operator isn't used much as a bitwise operator, but it can be used to see if two expressions are equivalent, that is, you can test to see if two expressions resolve to equivalent outcomes.

myInt1 =  4 myInt2 =  2

Statement (myInt1 < 5) EQV (myInt2 > 1) is equivalent.

Statement (myInt1 > 5) EQV (myInt2 < 1) is equivalent.

Statement (myInt1 > 5) EQV (myInt2 < 1) is not equivalent.

It is important to realize that you are not testing to see if the expressions are True or False. You are only testing to see if the expressions are equivalent to each other. For example, suppose you have two characters in a game and you want to attack with both characters, if they are at equivalent strength. You could build an expression similar to the one in the listing and take action based on the equivalence of the two characters.

The IMP Operator

Like the EQV operator, IMP is rarely used as a bitwise operator. It is used in logic programming to test whether assertion A implies assertion B. Looking at the truth table we can see that a True assertion implies a True conclusion so True and True is also True. A True assertion however cannot imply a False conclusion so True and False is False. A False premise can imply any conclusion so False with any conclusion is always True. Unless you are building an expert system or natural language interface, you will probably never need to use this operator.

Shortcut Bitwise Operators

The bitwise operators, like the arithmetic operators, can be written in shorthand form. The following table lists the shortcut versions of the bitwise operators.

Operator Syntax Comment
And= B And= C This is the same as B = B And C.
Or= B Or= C This is the same as B = B Or C.
Xor= B Xor= C This is the same as B = B Xor C.
Eqv= B Eqv = C This is the same as B = B Eqv C.
Imp= B Imp= C This is the same as B = B Imp C.

Exercise caution when using bitwise operators with arithmetic operators, as the result may not be what you expect. When used in logical expressions, such as in an If statement, make sure the bitwise operators operate on either the True or False values of complete expressions to avoid evaluation problems.

The SHL and SHR Operators

SHL stands for shift left and SHR stands for shift right. As the names imply, these operators shift the bits of a byte or integer-type variable either left or right. The following table shows the syntax diagram of both operators.

Operator Syntax Comments
SHL (Shift bits left) B = variable SHL number Shifts the bits of variable left number of places.
SHR (Shift bits right) B = variable SHR number Shifts the bits of variable right number of places.

Shifting left is the same as multiplying by 2 and shifting right is the same as dividing by 2. You can see this by looking at the binary representation of a number. Take the byte value of 1 which is 0000 0001. The 1 is in the 2^0 position. 2^0 equals 1. Shifting the bit left, makes 0000 0010, putting the 1 bit in the 2^1 position, which evaluates to 2. This is the same as 1*2. Shifting left again puts the bit at the 2^2 position, 0000 0100 which evaluates to 4, or 2 *2.

Shifting the bit right puts the 1 back in the 2^1 position, 0000 0010, which evaluates to 2, or 4/2. Shifting right again puts the 1 bit in the 20 position, 0000 0001, which is 1 or 2/2.

The shift operation can be used as a replacement for multiplication or division if you are working with powers of 2. It is also used to pack data items into variables, or to retrieve data items from variables. The following macro MAKDWORD packs two values into a single variable.

#define MAKDWORD(x,y) (cint(x) shl 16 or cint(y)).

In this macro, the value of x is converted to an integer, and then shifted left 16 bits into the high word. An integer is 4 bytes and can be represented as 00000000 00000000 00000000 00000001 . Shifting 16 bits left makes 00000000 00000001 00000000 00000000. Remember that a word is two bytes, so the 1 has been shifted to the high word of the integer. You can then use the Hiword function to retrieve this value.

Bitwise Macros

FreeBasic has several built-in macros for retrieving and setting bit and byte data from a variable. The following tables lists the macros, the syntax and their definitions.

Macro Syntax Definition
Hiword B = Hiword(variable) #define Hiword(x) (CUInt(x) shr 16)
Loword B = Loword(variable) #define Loword(x) (CUInt(x) and 65535)
Hibyte B = Hibyte(variable) #define Hibyte(x) ((CUint(x) and 65280) shr 8)
Lobyte B = Lobyte(variable) #define Lobyte( x ) ( CUint( x ) and 255 )
Bit B = Bit( variable, bit_number ) #define Bit( x, bit_number ) (((x) and (1 shl (bit_number))) > 0)
Bitset B = Bitset(variable, bit_number) #define Bitset( x, bit_number ) ((x) or (1 shl (bit_number)))
Bitreset B = Bitreset(variable, bit_number) #define Bitreset( x, bit_number ) ((x) and not (1 shl (bit_number)))

The Hiword macro returns the leftmost two bytes of an integer and the Loword macro returns the the rightmost two bytes. The Hibyte macro returns the leftmost eight bits of a an integer and the Lobyte returns the rightmost eight bits.

The Bit macro returns a -1 if a bit at position bit_number is a 1, otherwise it returns a 0. The Bitset macro sets the bit at position bit_number to 1 and returns the number, and the Bitreset macro sets the bit at position bit_number to 0 and returns the number. The rightmost bit is bit 0 following the binary numbering scheme.

These macros are useful when working with third party libraries such as the Windows API, where several pieces of information are stored in a single data type.

Bitwise operators will only work correctly on byte and integer-type data. A single or double-type variable that is passed to a bitwise operator will be implicitly converted to an integer, which may result in precision loss.