Difference between revisions of "Bitwise Operators"

From NWN Lexicon
Jump to: navigation, search
(Fixed an issue where the | symbol was messing up the table)
Line 9: Line 9:
 
! Operation
 
! Operation
 
|-
 
|-
| |
+
| |
 
| OR
 
| OR
 
|-
 
|-
Line 91: Line 91:
 
To use this effectively, you would have some preset values like this:
 
To use this effectively, you would have some preset values like this:
  
 +
<nwscript>
 
   
 
   
  <font color="#0000ff">int</font> QUEST_ONE_DONE = <font color="#7f007f">1</font>;
+
  int QUEST_ONE_DONE = 1;
  <font color="#0000ff">int</font> QUEST_TWO_DONE = <font color="#7f007f">2</font>;
+
  int QUEST_TWO_DONE = 2;
  <font color="#0000ff">int</font> QUEST_THREE_DONE = <font color="#7f007f">4</font>;
+
  int QUEST_THREE_DONE = 4;
  <font color="#0000ff">int</font> QUEST_FOUR_DONE = <font color="#7f007f">8</font>;
+
  int QUEST_FOUR_DONE = 8;
 +
</nwscript>
  
 
<br />
 
<br />
Line 109: Line 111:
 
# I want a cleaner, more efficient way of checking the status of my 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:  
 
# I check a local int on the PC that I have called 'nQuestFlags' with the following line:  
<font color="#007f00">// assumes the following code executes in one of the PC's
+
<nwscript>
  </font><font color="#007f00">// events; if not use, the appropriate Get* function to retrieve
+
// assumes the following code executes in one of the PC's
  </font><font color="#007f00">// the PC (in place of OBJECT_SELF)
+
  // events; if not use, the appropriate Get* function to retrieve
  </font><font color="#0000ff">int</font> nQuestFlags = GetLocalInt(<font color="#0000ff">OBJECT_SELF</font>, <font color="#00007f">"nQuestFlags"</font>);
+
  // the PC (in place of OBJECT_SELF)
  <font color="#0000ff">if</font> (nQuestFlags &amp; QUEST_TWO_DONE)  
+
  int nQuestFlags = GetLocalInt(OBJECT_SELF, "nQuestFlags");
 +
  if (nQuestFlags &amp; QUEST_TWO_DONE)  
 
  {  
 
  {  
       <font color="#007f00">// he did the quest  
+
       // he did the quest  
  </font>}  
+
  }  
  <font color="#0000ff">else</font>
+
  else  
 
  {  
 
  {  
       <font color="#007f00">// he did not do the quest  
+
       // he did not do the quest  
  </font>}
+
  }
 +
</nwscript>
 
# When the PC completes quest 3, I add the flag with this line:  
 
# When the PC completes quest 3, I add the flag with this line:  
<font color="#007f00">// again, assumes this executes in one of the PC's events
+
<nwscript>
  </font><font color="#007f00">// as OBJECT_SELF refers to the PC
+
// again, assumes this executes in one of the PC's events
  </font>nQuestFlags = nQuestFlags | QUEST_THREE_DONE;
+
  // as OBJECT_SELF refers to the PC
  SetLocalInt(<font color="#0000ff">OBJECT_SELF</font>, <font color="#00007f">"nQuestFlags"</font>, nQuestFlags);
+
  nQuestFlags = nQuestFlags | QUEST_THREE_DONE;
 +
  SetLocalInt(OBJECT_SELF, "nQuestFlags", nQuestFlags);
 +
</nwscript>
  
 
One final example to demonstrate the flexibility that bitwise operators and arithmatic provide appears below.
 
One final example to demonstrate the flexibility that bitwise operators and arithmatic provide appears below.
  
 +
<nwscript>
 
   
 
   
  <font color="#007f00">// prototype
+
  // prototype
  </font><font color="#0000ff">void</font> testQuest(<font color="#0000ff">int</font> nQuests);
+
  void testQuest(int nQuests);
  <font color="#007f00">// pseudo-constants (bit flags)
+
  // pseudo-constants (bit flags)
  </font><font color="#0000ff">int</font> QUEST_ONE_DONE = <font color="#7f007f">1</font>;
+
  int QUEST_ONE_DONE = 1;
  <font color="#0000ff">int</font> QUEST_TWO_DONE = <font color="#7f007f">2</font>;
+
  int QUEST_TWO_DONE = 2;
  <font color="#0000ff">int</font> QUEST_THREE_DONE = <font color="#7f007f">4</font>;
+
  int QUEST_THREE_DONE = 4;
  <font color="#0000ff">int</font> QUEST_FOUR_DONE = <font color="#7f007f">8</font>;
+
  int QUEST_FOUR_DONE = 8;
 
   
 
   
  <font color="#0000ff">void</font> main()
+
  void main()
 
  {
 
  {
     <font color="#007f00">// demonstrate the flexibility of bitwise operations
+
     // demonstrate the flexibility of bitwise operations
</font>    <font color="#0000ff">int</font> nQuest = <font color="#7f007f">0</font>;
+
    int nQuest = 0;
     <font color="#007f00">// let's complete quest one
+
     // let's complete quest one
</font>    nQuest = nQuest | QUEST_ONE_DONE;
+
    nQuest = nQuest | QUEST_ONE_DONE;
     <font color="#007f00">// let's now complete quest three
+
     // let's now complete quest three
</font>    nQuest |= QUEST_THREE_DONE; <font color="#007f00">// same as "nQuest = nQuest | QUEST_THREE_DONE;"
+
    nQuest |= QUEST_THREE_DONE; // same as "nQuest = nQuest | QUEST_THREE_DONE;"
</font>    <font color="#007f00">// what does the log print out?
+
    // what does the log print out?
</font>    testQuest(nQuest);
+
    testQuest(nQuest);
     <font color="#007f00">// examining the log, you would see
+
     // examining the log, you would see
</font>    <font color="#007f00">/*
+
    /*
 
         Quest one complete
 
         Quest one complete
 
         Quest three complete
 
         Quest three complete
     */</font>
+
     */
 
     testQuest(QUEST_ONE_DONE | QUEST_TWO_DONE | QUEST_THREE_DONE | QUEST_FOUR_DONE);
 
     testQuest(QUEST_ONE_DONE | QUEST_TWO_DONE | QUEST_THREE_DONE | QUEST_FOUR_DONE);
     <font color="#007f00">// examining the log for the above statement, you would see
+
     // examining the log for the above statement, you would see
</font>    <font color="#007f00">/*
+
    /*
 
         Quest one complete
 
         Quest one complete
 
         Quest two complete
 
         Quest two complete
 
         Quest three complete
 
         Quest three complete
 
         Quest four complete
 
         Quest four complete
     */</font>
+
     */
 
  }
 
  }
 
   
 
   
  <font color="#0000ff">void</font> testQuest(<font color="#0000ff">int</font> nQuests)
+
  void testQuest(int nQuests)
 
  {
 
  {
     <font color="#007f00">// writes the completed quest to the log file
+
     // writes the completed quest to the log file
</font>    <font color="#0000ff">if</font> (nQuests &amp; QUEST_ONE_DONE)
+
    if (nQuests &amp; QUEST_ONE_DONE)
         PrintString(<font color="#00007f">"Quest one complete"</font>);
+
         PrintString("Quest one complete");
     <font color="#0000ff">if</font> (nQuests &amp; QUEST_TWO_DONE)
+
     if (nQuests &amp; QUEST_TWO_DONE)
         PrintString(<font color="#00007f">"Quest two complete"</font>);
+
         PrintString("Quest two complete");
     <font color="#0000ff">if</font> (nQuests &amp; QUEST_THREE_DONE)
+
     if (nQuests &amp; QUEST_THREE_DONE)
         PrintString(<font color="#00007f">"Quest three complete"</font>);
+
         PrintString("Quest three complete");
     <font color="#0000ff">if</font> (nQuests &amp; QUEST_FOUR_DONE)
+
     if (nQuests &amp; QUEST_FOUR_DONE)
         PrintString(<font color="#00007f">"Quest four complete"</font>);
+
         PrintString("Quest four complete");
 
  }
 
  }
 +
</nwscript>
  
 +
<nwscript>
 
  '''Bitwise AND and OR in logical evaluations (if/then-type tests)'''<br />
 
  '''Bitwise AND and OR in logical evaluations (if/then-type tests)'''<br />
 +
</nwscript>
  
 
Instead of using the short-circuit logical evaluators and ("&amp;&amp;") and or ("||") you can use bitwise AND ("&amp;") 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 arithmatic (see above).
 
Instead of using the short-circuit logical evaluators and ("&amp;&amp;") and or ("||") you can use bitwise AND ("&amp;") 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 arithmatic (see above).
  
 +
<nwscript>
 
   
 
   
  <font color="#007f00">// these two simple functions are used for demonstrating bitwise
+
  // these two simple functions are used for demonstrating bitwise
  </font><font color="#007f00">// AND and OR
+
  // AND and OR
  </font><font color="#0000ff">int</font> LogicalTestOne(<font color="#0000ff">int</font> iReturn)
+
  int LogicalTestOne(int iReturn)
 
  {
 
  {
     PrintString(<font color="#00007f">"Test One Ran..."</font>);
+
     PrintString("Test One Ran...");
     <font color="#0000ff">return</font> iReturn;
+
     return iReturn;
 
  }
 
  }
 
   
 
   
  <font color="#0000ff">int</font> LogicalTestTwo(<font color="#0000ff">int</font> iReturn)
+
  int LogicalTestTwo(int iReturn)
 
  {
 
  {
     PrintString(<font color="#00007f">"Test Two Ran..."</font>);
+
     PrintString("Test Two Ran...");
     <font color="#0000ff">return</font> iReturn;
+
     return iReturn;
 
  }
 
  }
 +
</nwscript>
  
 
The following code sample uses the above defined functions and outputs to the log:
 
The following code sample uses the above defined functions and outputs to the log:
  
 +
<nwscript>
 
   
 
   
  <font color="#0000ff">void</font> main()
+
  void main()
 
  {
 
  {
       <font color="#0000ff">if</font> (LogicalTestOne(<font color="#7f007f">1</font>) &amp; LogicalTestTwo(<font color="#7f007f">2</font>))
+
       if (LogicalTestOne(1) &amp; LogicalTestTwo(2))
 
       {
 
       {
           PrintString(<font color="#00007f">"Both tests returned true."</font>);
+
           PrintString("Both tests returned true.");
 
       }
 
       }
 
  }
 
  }
 +
</nwscript>
  
 
<br />
 
<br />
Line 210: Line 224:
 
Examining the log, we find the following:
 
Examining the log, we find the following:
  
 +
<nwscript>
 
   
 
   
 
  Test One Ran...
 
  Test One Ran...
 
  Test Two Ran...
 
  Test Two Ran...
 +
</nwscript>
  
 
<br />
 
<br />

Revision as of 20:26, 25 August 2012

Bitwise Operators

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.


Bitwise Operators

Symbol Operation
OR
& AND
~ NOT
^ XOR (exclusive OR)
<< Shift Left
>> Shift Right
>>> Shift Right Zero Fill


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


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


Bitwise 'OR' operator ("|"):

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


Bitwise 'AND' operator ("&"):

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


Bitwise 'NOT' operator ("~"):

Will compare the bits of two numbers, and return a number with 1s where neither number has a 1. This is used to subtract a bit from a number.

Example: 0001 1010 ~ 0100 0011 = 1010 0100


Bitwise 'XOR' operator ("^"):

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


Bitwise 'shift left' operator ("<<"):

Will take the bits of a number and move the 1s to the left a certain number of places. This is used to multiply by powers of 2.

Example: 0001 1010 << 2 = 0110 1000


Bitwise 'shift right' operator (">>"):

Will take the bits of a number and move the 1s to the right a certain number of places. This is used to divide by powers of 2.

Example: 0001 1010 >> 2 = 1000 0110


Bitwise 'shift right zero fill' operator (">>>"):

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 byte are discarded, and the left side of the byte is padded with zeros (zero bits). This is used to divide by powers of 2.

Example: 0100 1111 >>> 2 = 0001 0011


Why on earth do I 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:

 
 int QUEST_ONE_DONE = 1;
 int QUEST_TWO_DONE = 2;
 int QUEST_THREE_DONE = 4;
 int QUEST_FOUR_DONE = 8;


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.


A practical example using the values set above.

  1. I want to know if a PC has done a particular quest.
  2. I want to eliminate the number of variables stored on the PC that depict the state of the quests.
  3. I want a cleaner, more efficient way of checking the status of my quests.
  4. 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 &amp; QUEST_TWO_DONE) 
 { 
      // he did the quest 
 } 
 else 
 { 
      // he did not do the quest 
 }
  1. 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);

One final example to demonstrate the flexibility that bitwise operators and arithmatic provide appears below.

 
 // 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()
 {
     // demonstrate the flexibility of bitwise operations
     int nQuest = 0;
     // let's complete quest one
     nQuest = nQuest | QUEST_ONE_DONE;
     // let's now 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)
 {
     // writes the completed quest to the log file
     if (nQuests &amp; QUEST_ONE_DONE)
         PrintString("Quest one complete");
     if (nQuests &amp; QUEST_TWO_DONE)
         PrintString("Quest two complete");
     if (nQuests &amp; QUEST_THREE_DONE)
         PrintString("Quest three complete");
     if (nQuests &amp; QUEST_FOUR_DONE)
         PrintString("Quest four complete");
 }
 '''Bitwise AND and OR in logical evaluations (if/then-type tests)'''<br />

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 arithmatic (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) &amp; 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