# Difference between revisions of "Bitwise Operators"

(Add assignment operators with examples) |
(Correct NOT usage) |
||

Line 87: | Line 87: | ||

===="NOT" ("~"):==== | ===="NOT" ("~"):==== | ||

− | + | Unlike the other operators, this is a unary operator, which means it acts on a single number. It returns the number's bitwise complement (i.e., a number with 1s where there were 0s and vice versa). It is used to invert the bits of a number. | |

− | Example: | + | Example: ~0011 = 1100 |

====XOR ("^"):==== | ====XOR ("^"):==== |

## Revision as of 08:42, 29 July 2020

These operators obtain results based on the logical relationships of individual bits within a binary counting system. Bitwise logic is also known as Boolean logic or Boolean mathematics.

## Contents

### Operators

Symbol | Operation |
---|---|

| | OR |

& | AND |

~ | NOT |

^ | XOR (exclusive OR) |

<< | Shift Left |

>> | Shift Right truncate remainder |

>>> | Shift Right pad with first bit |

The bitwise AND ("&") and bitwise OR ("|") are briefly detailed in the logical operators topic. Additional information on these operators appear below.

Each of the above operators also has an equivalent assignment operator:

Symbol | Operation | Example | Equivalent |
---|---|---|---|

|= | OR Assignment | x |= 3 | x = x | 3 |

& | AND Assignment | x &= 3 | x = x & 3 |

^= | XOR Assignment | x ^= 3 | x = x ^ 3 |

<<= | Shift Left Assignment | x <<= 3 | x = x << 3 |

>>= | Shift Right Assignment | x >>= 3 | x = x >> 3 |

>>>= | Shift Right Zero Fill Assignment | x >>>= 3 | x = x >>> 3 |

### Usage Examples

For our examples, let's use 8-bit binary numbers to practice. They are represented as 4 bits, a space, and the last four bits.

Example: 0001 1010 = 26

#### OR ("|"):

Will compare the bits of two numbers, and return a number with a 1 in every bit that has a 1 in either number. This is basically used to "add" a bit to a number.

Example: 0001 1010 | 0001 0101 = 0001 1111

#### AND ("&"):

Will compare the bits of two numbers, and return a number with a 1 in every bit that has a 1 in both numbers. This is used to see if a bit exists in a number.

Example: 0001 1010 & 0000 1000 = 0000 1000

#### "NOT" ("~"):

Unlike the other operators, this is a unary operator, which means it acts on a single number. It returns the number's bitwise complement (i.e., a number with 1s where there were 0s and vice versa). It is used to invert the bits of a number.

Example: ~0011 = 1100

#### XOR ("^"):

Will compare the bits of two numbers, and return a number with a 1 in every bit that has a 1 in one number, but not the other. This is used to toggle a bit in a number.

Example: 0001 1010 ^ 0001 0000 = 0000 1010 and 0001 1010 ^ 0000 0100 = 0001 1110

#### shift left ("<<"):

Will take the bits of a number and move the 1s to the left a certain number of places. The bits that pass the left bound are discarded, and the right side of the bit field is padded with zeros. This is used to multiply by powers of 2.

Example: 0001 1010 << 2 = 0110 1000

#### shift right (">>"):

Will take the bits of a number and move the 1s to the right a certain number of places discarding bits that pass right bound and padding the left of the bit field with zeroes. Negative numbers are converted to positive before the shift and back to negative after the shift. This is used to divide by powers of 2 truncating the remainder.

Example: 0001 1010 >> 2 = 0000 0110

#### shift right pad with first bit (">>>"):

Will take the bits of a number and move the 1s to the right a certain number of places. The ones and zeros (bits) that pass the right bound of the bit field are discarded, and the left side of the bit field is padded with the value of the first bit. This is used to divide by powers of 2, rounding down any remainder.

Example: 0100 1111 >>> 2 = 0001 0011

### Practical Examples

Why on earth do you want to know this?

The trick to using bitwise arithmetic is to stop thinking of the numbers as numbers, but as groups of Yes/No flags. Each bit can represent a TRUE/FALSE value which you would check against. To use this effectively, you would have some preset values like this:

Notice that each value is a power of 2. This means it can be represented by a binary number with only a single 1 in place. That place value is the location for the "flag" that represents a certain TRUE/FALSE value.

#### Setting Quest States

A practical example using the values set above:

- I want to know if a PC has done a particular quest.
- I want to eliminate the number of variables stored on the PC that depict the state of the quests.
- I want a cleaner, more efficient way of checking the status of my quests.
- I check a local int on the PC that I have called "nQuestFlags" with the following line:
// assumes the following code executes in one of the PC's // events; if not, use the appropriate Get* function to retrieve // the PC (in place of OBJECT_SELF) int nQuestFlags = GetLocalInt(OBJECT_SELF, "nQuestFlags"); if (nQuestFlags & QUEST_TWO_DONE) { // he did the quest } else { // he did not do the quest }

- When the PC completes quest 3, I add the flag with this line:
// again, assumes this executes in one of the PC's events // as OBJECT_SELF refers to the PC nQuestFlags = nQuestFlags | QUEST_THREE_DONE; SetLocalInt(OBJECT_SELF, "nQuestFlags", nQuestFlags);

#### Retrieving Quest States

One final example to demonstrate the flexibility that bitwise operators and arithmetic provide:

// prototype void testQuest(int nQuests); // pseudo-constants (bit flags) int QUEST_ONE_DONE = 1; int QUEST_TWO_DONE = 2; int QUEST_THREE_DONE = 4; int QUEST_FOUR_DONE = 8; void main() { int nQuest; // Let's complete quest one nQuest = nQuest | QUEST_ONE_DONE; // Now let's complete quest three nQuest |= QUEST_THREE_DONE; // same as "nQuest = nQuest | QUEST_THREE_DONE;" // What does the log print out? testQuest(nQuest); // Examining the log, you would see /* Quest one complete Quest three complete */ testQuest(QUEST_ONE_DONE | QUEST_TWO_DONE | QUEST_THREE_DONE | QUEST_FOUR_DONE); // Examining the log for the above statement, you would see /* Quest one complete Quest two complete Quest three complete Quest four complete */ } void testQuest(int nQuests) { // Write the completed quest to the log file if (nQuests & QUEST_ONE_DONE) PrintString("Quest one complete"); if (nQuests & QUEST_TWO_DONE) PrintString("Quest two complete"); if (nQuests & QUEST_THREE_DONE) PrintString("Quest three complete"); if (nQuests & QUEST_FOUR_DONE) PrintString("Quest four complete"); }

### Bitwise AND and OR in logical evaluations (if/then-type tests)

Instead of using the short-circuit logical evaluators AND ("&&") and OR ("||"), you can use bitwise AND ("&") and OR ("|") in their place, assuming the default NWN values of TRUE ("1") and FALSE ("0") are used. Of course using these operators on statements that do not return a TRUE or FALSE will perform mathematical operations that will not give you the results you seek unless you are specifically trying to use bitwise arithmetic (see above).

// These two simple functions are used for demonstrating bitwise // AND and OR int LogicalTestOne(int iReturn) { PrintString("Test One Ran..."); return iReturn; } int LogicalTestTwo(int iReturn) { PrintString("Test Two Ran..."); return iReturn; }

The following code sample uses the above defined functions and outputs to the log:

void main() { if (LogicalTestOne(1) & LogicalTestTwo(2)) { PrintString("Both tests returned true."); } }

Examining the log, we find the following:

Test One Ran... Test Two Ran...

But we expect to see "Both tests returned true." in the log file as well! What happened?

To understand this you need to understand what the values of 1 and 2 are and how the bitwise operator works. First the value of 1 in binary is 0001 and the value of 2 is 0010. The bitwise AND returns a 1 or a 0 if both positions of the comparitors are 1. In the case of 1 & 2 the result is 0:

0001

0010

----

0000

More bitwise fun comes with the | operator. What the OR operator does is return a 1 if either of the bits in the position are 1. So evaluating 0 | 2 results in:

0000

0010

----

0010

Which is 2. Now for the fun part. By definition 0 is FALSE and all other values resolve to NOT-FALSE or TRUE (even negative numbers; only 0 is FALSE). This can be evidenced by running the script with the values of 2 & 3:

0010

0011

----

0010

And 0010 is 2 which resolves to TRUE. The danger here occurs when you test numbers like 1 & 2:

0001

0010

----

0000

Or 3 & 4:

0011

0100

----

0000

Or 7 & 8:

0111

1000

----

0000

However, going back to my original note, IF you stick with the predefined values (as defined in nwscript.nss) of FALSE (0) and TRUE (1) you can safely exchange && with &. While it is true that the bitwise operators are different creatures than logical operators, informed developers can and do exchange them. The secret is understanding what they do.

author: Ryan Hunt, editor: Charles Feduke, additional contributor(s): Joseph Berkley