Bitwise operators

From ZCWiki
Jump to: navigation, search

Bitwise operators operate on each bit in a number, instead of the number as a whole. This concept may be unfamiliar to those who have not programmed in C or a similar language before, but it does have its place in ZScript so a few of the more useful tricks are presented here.

A Quick Note on Binary

There are many resources describing binary, how to convert from decimal (our typical way of writing numbers) to binary, and so forth, so these concepts are not presented here. However, since bitwise operators cannot be discussed without presenting numbers in binary, a few quick notes are required.

For the sake of this article, it may suffice to present the following reference table, which shows some decimal numbers and their binary equivalents. These numbers will be used in the examples in this article. Bolded rows indicate cases where only a single bit in the binary equivalent is set.

Decimal Number Binary Number
1 0001
2 0010
4 0100
6 0110
8 1000
10 1010
12 1100
14 1110
15 1111

Syntax and Operation

Bitwise AND

The Bitwise AND operator & takes two numbers (called operands) and compares them bit-by-bit. For each bit position, the associated bit in the result is 1 only if both corresponding bits in the operands are 1. Conversely, if either bit in the operands is 0, the associated bit in the result will be 0.

An example of how to use the Bitwise AND is shown here:

int X = 0110b; // 6 decimal.
int Y = 1100b; // 12 decimal.
int Z;

Z = X & Y;  // 0110 ANDed with 1100 is 0100 (4 decimal).

In the above example, the values of X and Y are compared, bit by bit (note that the values of X and Y are specified in binary). This means that the right-most bit of X (0) is ANDed with the right-most bit of Y (0). Since for the AND operation both bits must be 1 for the result to be 1, in this case the right-most bit of the result, stored in variable Z, is 0. The operation then moves to the second bit from the right, and so forth until all bits have been checked. In this particular case, only the third bit from the right is 1 in both numbers, so in the result only the third bit from the right is 1. Using the conversion table above, the result in decimal (base 10) is 4.

It may help to visualize this by arranging the bits so they line up in columns, as if you were going to add them. If any number in the column is a zero, then the AND result for that column is also zero.

  0110
& 1100
------
  0100

Note that the AND operator does not require the operands to be specified in binary. The following code produces the same result, even though the values are specified in decimal.

int X = 6;  // 0110 binary.
int Y = 12; // 1100 binary.
int Z;

Z = X & Y;  // 6 ANDed with 12 is 4 (0100 binary).

Do not confuse the bitwise AND "&" with the logical AND "&&".

Bitwise OR

The Bitwise OR operator | takes two numbers (called operands) and compares them bit-by-bit. For each bit position, the associated bit in the result is 1 if either of the corresponding bits in the operands are 1. Conversely, if both bits in the operands are 0, the associated bit in the result will be 0.

An example of how to use the Bitwise OR is shown here:

int X = 0110b; // 6 decimal.
int Y = 1100b; // 12 decimal.
int Z;

Z = X | Y;  // 0110 ORed with 1100 is 1110 (14 decimal).

In the above example, the values of X and Y are compared, bit by bit (note that the values of X and Y are specified in binary). This means that the right-most bit of X (0) is ORed with the right-most bit of Y (0). For the OR operation either bit must be 1 for the result to be 1, so in this case the right-most bit of the result, stored in variable Z, is 0. The operation then moves to the second bit from the right, and so forth until all bits have been checked. In this particular case, all but the right-most bit positions have a 1 in at least one of the operands, so the result is all 1s with the exception of the right-most bit. Using the conversion table above, the result in decimal (base 10) is 14.

It may help to visualize this by arranging the bits so they line up in columns, as if you were going to add them. If any number in the column is a 1, then the OR result for that column is also 1.

  0110
| 1100
------
  1110

Do not confuse the bitwise OR "|" with the logical OR "||".

Bitwise XOR

The Bitwise XOR operator ^ compares the individual bits of two bit patterns. (The patterns must be of equal length, for a comparison to produce meaningful results.) This operation will produce a result of 1 if the bit in one pair is 1, and the corresponding bit in the second pattern is 0; if both bits match, either '1' or '0', the result will be 0.

An example of how to use the Bitwise XOR is shown here:

int X = 0110b; // 6 decimal.
int Y = 1100b; // 12 decimal.
int Z;

Z = X ^ Y;  // 0110 XORed with 1100 is 1010 (10 decimal).

It may help to visualize this by arranging the bits so they line up in columns, as if you were going to add them. If any number from a column of the first (upper) value, matches the same number in that column of the second (lower) value, then the XOR result for that column is 0. If it is different, then the XOR result for that column is 1.

  0110
^ 1100
------
  1010

Bitwise NOT

The Bitwise NOT operator ~ takes one number (called an operand) and changes each bit in that number to the opposite value - in other words, all bits that are zero are set to one, and all bits that are one are set to zero.

An example of how to use the Bitwise NOT is shown here:

int Y = 1100b; // 12 decimal.
int Z;

Z = ~Y;  // 1100 NOTed is 0011 (3 decimal).

In the above example, Z is set to ~Y. For each bit in Y, a zero is changed to a one and a one is changed to a zero. Since Y is 1100, ~Y is 0011.

Left Shift

To be supplied.

Right Shift

To be supplied.

Practical Uses of Bitwise Operators in ZScript

Determining Combo Walkability

The Screen->ComboS[i] property returns the "walkability" of the ith Combo on the screen. In ZQuest, a Combo is divided into 4 smaller squares, and you can set each square to allow or prevent Link from walking on it. You can access this information in ZScript using the Screen->ComboS[i] property, but you get a number back. So how do you figure out which of the 4 quadrants of a Combo are walkable based on this number?

You can use the Bitwise AND operator. The ZScript.txt file provides some insight. It states:

The least significant bit is true if the top-left of the combo is solid, the second-least significant bit is true if the bottom-left of the combo is is solid, the third-least significant bit is true if the top-right of the combo is solid, and the fourth-least significant bit is true if the bottom-right of the combo is solid.

What this means is that if the top-left quadrant of the combo is not walkable, the right-most bit of the whatever Screen->ComboS[i] returns will be '1'. If the bottom-left quadrant of the combo is not walkable, then the second bit from the right will be '1', and so forth. This gives us a "bit pattern" or "bit mask" for determining walkability:

// Note that we're declaring these numbers in binary.
int Top_Left_Walkability = 0001b;
int Bottom_Left_Walkability = 0010b;
int Top_Right_Walkability = 0100b;
int Bottom_Right_Walkability = 1000b;

Use these bit masks to isolate the proper bit in the Screen->ComboS[x] result and check its value. Here's an example to determine if the top left quadrant of a the first combo on the screen is walkable.

int Is_Top_Left_Walkable;

Is_Top_Left_Walkable = Screen->ComboS[1] & Top_Left_Walkability;
if (Is_Top_Left_Walkable == Top_Left_Walkability) {
  // Then the Top Left quadrant of the Combo is sold - it is not walkable.
}

This works because ANDing 0001 (the Top_Left_Walkability mask) with Screen->ComboS[1] sets all the bits to zero except for the right-most bit, which is the one you want to check to determine if the top left quadrant is walkable. At this point, the value stored in Is_Top_Left_Walkable will either be 0000 or 0001, depending on the value of Screen->ComboS[1]. If Is_Top_Left_Walkable is equal to Top_Left_Walkability, that means that the top left quadrant is not walkable.

Note that you can shorten the code snippet above by skipping the step of storing the AND result in a variable; perform the AND operation in the if statement:

if ((Screen->ComboS[1] & Top_Left_Walkability) == Top_Left_Walkability) {
  // Top Left quadrant isn't walkable.
}

See Also