Bitwise operators are used to manipulate integers that represent bitfields. Click on an operator below to scroll directly to the position of its description.

Operator | Name | Usage Reference |

& | AND | integer & integer = integer |

| | OR | integer | integer = integer |

~ | NOT | ~integer = integer |

^ | XOR | integer ^ integer = integer |

>> | Right-shift | integer >> number of bits = integer |

<< | Left-shift | integer << number of bits = integer |

The AND operation compares each bit in two integers; if both bits are set to "1", then the final result will be "1", otherwise it will be "0". This is done "per-bit", or "bitwise".

Truth Table:

AND | 1 | 0 |

1 | 1 | 0 |

0 | 0 | 0 |

Usage:

// A bitfield whose [[binary]] representation is: 00000000000000000000000000001010 integer a = 10; // Another bitfield whose binary representation is: 00000000000000000000000000011001 integer b = 25; // The next line translates to "store the result of AND'ing a and b together" in the variable a_AND_b. // Its binary representation is 00000000000000000000000000001000, or 8. integer a_AND_b = a & b;

a 00000000000000000000000000001010 // As decimal = 10 b 00000000000000000000000000011001 // As decimal = 25 ----------------------------------------------------------------- a & b 00000000000000000000000000001000 // As decimal = 8

For example, if the user is holding down the up arrow:

control(key controller, integer levels, integer edges) { // If user is holding down up arrow: if ((levels & CONTROL_FWD) == CONTROL_FWD) { // Do stuff... } }

control(key controller, integer levels, integer edges) { // If user is holding down up arrow: if ((levels & CONTROL_FWD) == CONTROL_FWD) { // The above translates to: "If the CONTROL_FWD bit is 1 in the levels bitfield" } // If user is holding down down arrow: if ((levels & CONTROL_BACK) == CONTROL_BACK) { // The above translates to: "If the CONTROL_BACK bit is 1 in the levels bitfield" } }

levels 00000000000000000000000000000011 // As decimal = 3 CONTROL_BACK 00000000000000000000000000000010 // As decimal = 2 ------------------------------------------------------------------------------- levels & CONTROL_BACK 00000000000000000000000000000010 // As decimal = 2

OR places a 1 in the bitfield if either bitfields passed to it have a 1 in that position. If neither has a 1, it places a 0 in that position.

Truth Table:

OR | 1 | 0 |

1 | 1 | 1 |

0 | 1 | 0 |

Usage:

// A bitfield whose binary representation is: 00000000000000000000000000001010 integer a = 10; // Another bitfield whose binary representation is: 00000000000000000000000000011001 integer b = 25; // The next line translates to "store the result of OR'ing a and b together" in the variable a_OR_b. // Its binary representation is 00000000000000000000000000011011, or 27. integer a_OR_b = a | b;

a 00000000000000000000000000001010 // As decimal = 10 b 00000000000000000000000000011001 // As decimal = 25 -------------------------------------------------------------------- a | b 00000000000000000000000000011011 // As decimal = 27

control(key controller, integer levels, integer edges) { // If user is pressing both the up arrow and down arrow: if ((levels & (CONTROL_FWD | CONTROL_BACK)) == (CONTROL_FWD | CONTROL_BACK)) { // The above statement translates to: "If levels has both the CONTROL_FWD and CONTROL_BACK bits set to 1" } }

Written out in longhand, the conditional statement looks like this:

The ORing operation, to get a bitfield with both

CONTROL_FWD 00000000000000000000000000000001 // As decimal = 1 CONTROL_BACK 00000000000000000000000000000010 // As decimal = 2 ----------------------------------------------------------------------------------------- (CONTROL_FWD | CONTROL_BACK) 00000000000000000000000000000011 // As decimal = 3

The ANDing operation, to see if the both

In this case,

(CONTROL_FWD | CONTROL_BACK) 00000000000000000000000000000011 // As decimal = 3 levels 00000000000000000000000000010011 // As decimal = 19 ----------------------------------------------------------------------------------------- levels & (CONTROL_FWD | CONTROL_BACK) 00000000000000000000000000000011 // As decimal = 3

Back to top

NOT is unlike the other bitwise operators in that it does not perform an operation on two bitfields. It only performs an operation on one. What it does is reverse the bitfield; if a bit was previously 0, it is changed to 1, and vice versa.

Truth Table:

NOT | |

1 | 0 |

0 | 1 |

Usage:

// A bitfield whose binary representation is: 00000000000000000000000000001010 integer a = 10; // The next line translates to "store the result of NOTing a" in the variable NOT_a. // Its binary representation is 11111111111111111111111111110101, or -11. integer NOT_a = ~a;

a 00000000000000000000000000001010 // As decimal = 10 ----------------------------------------------------------------------- ~a 11111111111111111111111111110101 // As decimal = -11

NOT is commonly used to set bits to 0 in bitfields. To do this, a bit is NOTed to get ~bit. The bitfield is then ANDed with the ~bit.

For example, lets say you wanted to turn off 00000000000000000000000000000010 (2 in decimal) in the bitfield 00000000000000000000000000011010 (26 in decimal).

First you'd get ~00000000000000000000000000000010, which is 11111111111111111111111111111101 (-3 in decimal) then you'd AND ~2 and 26. Here is this in code:

integer bitfield = 26; integer bit = 2; bitfield = bitfield & ~bit;

Here are the operations written out longwise:

2 00000000000000000000000000000010 ----------------------------------------------------------------------- ~2 11111111111111111111111111111101 // As decimal = -3 26 00000000000000000000000000011010 ~2 11111111111111111111111111111101 ----------------------------------------------------------------------- 26 & ~2 00000000000000000000000000011000 // As decimal = 24

Back to top

Short for "eXclusive OR", XOR sets a bit to 1 in the resulting bitfield if, and only if, a bit in that position is set in one of the bitfields passed to it, and a bit in that position is not set in the other bitfield passed to it. It only sets a bit if the bits of the bitfields passed to it arent the same.

Truth Table:

XOR | 1 | 0 |

1 | 0 | 1 |

0 | 1 | 0 |

Usage:

// A bitfield whose binary representation is: 00000000000000000000000000001010 integer a = 10; // Another bitfield whose binary representation is: 00000000000000000000000000011001 integer b = 25; // The next line translates to "store the result of XORing a and b together" in the variable a_XOR_b. // Its binary representation is 00000000000000000000000000010011, or 19. integer a_XOR_b = a ^ b;

a 00000000000000000000000000001010 // As decimal = 10 b 00000000000000000000000000011001 // As decimal = 25 ----------------------------------------------------------------------- a ^ b 00000000000000000000000000010011 // As decimal = 19

XOR is useful when you want to execute code when one condition is true, and one is not. For example, if the up arrow is pressed when the right arrow is not or vice versa.

control(key controller, integer levels, integer edges) { // If user is pressing CONTROL_FWD but not CONTROL_RIGHT, or vice versa. if (((levels & CONTROL_FWD) == CONTROL_FWD) ^ ((levels & CONTROL_RIGHT) == CONTROL_RIGHT)) { // The above translates to: "If CONTROL_FWD is being pressed and CONTROL_RIGHT is // not being presed, or CONTROL_FWD is not being pressed and CONTROL_RIGHT is being pressed. } }

When doing bitwise math, XOR works as follows:

4 ^ 2 = 6 in decimal 4 = 100 in binary 2 = 10 in binary

100 010 ------ 110

6 ^ 2 = 4 in decimal 6 = 110 in binary 2 = 10 in binary

110 010 100

A last use for XOR is switching variables. Usually, when you switch variables, you use a temporary variable to hold one place, but with XOT, this temporary variable can be avoided. An example:

Traditional Method: x = 5 y = 7 w = x x = y y = w x = 7 y = 5

The variable w takes up space and can be avoided with XOR.

XOT method: x = 5 y = 7 x = x ^ y y = x ^ y x = x ^ y x = 7 y = 5 Binary: 0101 0111 ------ x = 0010 = 2 0010 0111 ------ y = 0101 = 5 0010 0101 ------ x = 0111 = 7

Therefore, x and y are switched without a temporary variable.

Last but not least you can use XOR to turn off bits in a field, without needing to AND a bitfield with a ~bit as described above:

XOR instead of bitfield AND ~bit: Lets say, we want to turn of two bits (lets say the 2nd and the 5th) in the field 00000000000000000000000000011010 To do so, we XOR that with a field where exactly the bits are set, which we want to turn off: 00000000000000000000000000011010 00000000000000000000000000010010 ------------------------------------- XOR 00000000000000000000000000001000

That can be usefull to switch on/off alot of true/false settings (flags) in only one global variable, what can speed up a script in some tight cases.

Back to top

This shifts all the bits in an integer to the right by the number of bits specified. Bits shifted out on the right (least significant) end of the word are discarded. Bits shifted in on the left (most significant) end of the word are copies of the word's original most significant bit (sign bit).

Apart from using it for shifting values around in bitfields, this is useful for doing very fast integer divison of positive number by powers of 2. Each bit shifted to the right equals a division by 2. Note that this will always round towards negative infinity; for positive numbers this is the same as rounding down towards zero, but has odd effects on negative numbers. For example,

Useage:

// shifts the integer 523 to the right by 2 bits, storing the result in foo integer foo = 523 >> 2;

Longwise:

a 00000000000000000000001000001011 // decimal 523 ---------------------------------------------------------- a >> 2 00000000000000000000000010000010 // decimal 130

Back to top

This shifts all the bits in an integer to the left by the number of bits specified. Bits will be cut off on the left (most significant) and filled up with 0 on the right.

Apart from using it for shifting values around in bitfields, this is useful for doing very fast integer multiplication by powers of 2. Each bit shifted to the left equals a multiplication by 2. Note that on an overflow on the left, bits will either fall into the value's sign bit (the most significant bit in the 32-bit word) or simply be cut off; in either case, the result will probably not be what you expected.

Useage:

// shifts the integer 523 to the left by 2 bits, storing the result in foo integer foo = 523 << 2;

Longwise:

a 00000000000000000000001000001011 // decimal 523 ---------------------------------------------------------- a << 2 00000000000000000000100000101100 // decimal 2092

Back to top

Operators | Unary | Binary | Bitwise | Boolean | Equality | Assignment

Comments [Hide comments/form]

This doesn't tell what they -do- though.

-- GuzarFonzarelli (2004-01-22 21:49:54)

I don't wanna get into an edit war, but why not have BooleanLogic and this page be the same? -- Guzar

Why not seperate them, one is a referance, the other is a tutorial

Why not seperate them, one is a referance, the other is a tutorial

-- HiroYamamoto (2004-01-22 22:03:12)

They are both references, I think. A tutorial is more of a walkthrough of how to implement something. Besides, everyone who knows what "the bitwise AND of the left and right hand side" means already knows the syntax for doing so.

-- GuzarFonzarelli (2004-01-23 00:10:12)

For those who want a logical right shift as opposed to an arithmetic one (SHR instead of SAR for us x86 assembly propeller-heads out there ;) ), you have to do something like this:

[code]

integer shr(integer value, integer count) {

if (value >= 0)

return value >> count;

else

return (value & 0x7FFFFFFF) >> count | 1 << (31 - count);

}

[/code]

We really need unsigned types in LSL. :-/

[code]

integer shr(integer value, integer count) {

if (value >= 0)

return value >> count;

else

return (value & 0x7FFFFFFF) >> count | 1 << (31 - count);

}

[/code]

We really need unsigned types in LSL. :-/

-- TalarusLuan (2006-04-30 02:29:07)

In response to Talarus:

You can also just mask out the extended sign bits, something like:

value = 0xf0000000;

value = (value >> 24) & 0xff;

Of course, that only works easily if you know in advance how much you're shifting by.

But it works great for encoding data into an integer to pass along, say in a rez_object event.

Say you have 4 1-byte values you need to pass to a new object:

on_rez(integer param)

{

integer first_param = param & 0xff;

integer second_param = (param >> 8) & 0xff;

integer third_param = (param >> 16) & 0xff;

integer fourth_param = (param >> 24) & 0xff;

}

You've essentially performed a logical shift on the fourth_param assignment, because you mask out the extended bits. For situations where you know the number of bits ahead of time, this is simpler.

But you are absolutely correct... we need unsigned.

You can also just mask out the extended sign bits, something like:

value = 0xf0000000;

value = (value >> 24) & 0xff;

Of course, that only works easily if you know in advance how much you're shifting by.

But it works great for encoding data into an integer to pass along, say in a rez_object event.

Say you have 4 1-byte values you need to pass to a new object:

on_rez(integer param)

{

integer first_param = param & 0xff;

integer second_param = (param >> 8) & 0xff;

integer third_param = (param >> 16) & 0xff;

integer fourth_param = (param >> 24) & 0xff;

}

You've essentially performed a logical shift on the fourth_param assignment, because you mask out the extended bits. For situations where you know the number of bits ahead of time, this is simpler.

But you are absolutely correct... we need unsigned.

-- DarksideEldrich (2006-07-12 12:47:01)

Attach a comment to this page: