Right Shift
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
Unsigned vs. Arithmetic
There are two types of Right Shifts, that can be performed on an integer. They are: Unsigned and Arithmetic. LSL currently only supports Arithmetic. The difference between the two modes is how it fills the bits revealed. With the unsigned mode, the revealed bits are always zero; in Arithmetic mode, it duplicates the old top bit to all the new bits. If you take the expression value >> count
where value is arithmetically shifted right count bits, then this is the same mathematically as doing value / (2count)
or in LSL value / llPow(2.0, count)
(but will result in data loss in LSL).
How to do Unsigned Right Shifts in LSL
Since LSL does not have a unsigned right shift operator you have to do it yourself. There are two methods for doing this, each with it's advantages and disadvantages.
There is a feature suggestion to add an unsigned right shift operator to LSL: SVC-1171
Method 1
This method is good when count is dynamic (not known at compile time) but is twice as complicated as Method 2.
((value & 0x7fFFffFF) >> count) - ((value & 0x80000000) >> count);
This works by shifting body and sign bits separately and then recombining them. Since the sign bit is extended as a negative number, we just need to make it positive. Since the left and right values have no bits in common (due to the masking), we can use | or + and they will do the same thing. To simplify the code we merge the negation and combine into a single operation.
Method 2
This method can only be used when count is a constant value. It works by using a constant mask to remove the extended sign bits.
(value >> count) & mask;
Example: (value >> 5) & 0x07FFFFFF
As you can see the top five bits have been turned off in the mask value, if you have trouble seeing that, you can just use the lookup table below.
|
|
|
Example Unsigned Right Shift Function
Here's a function that encapsulates the first method. It allows for a signed 32 bit integer along with a value indicating how far to shift the bits to be executed as an unsigned right shift.
// the lsl right shift is an arithmetic right shift,
// this means it more closely resembles dividing by a
// positive power of two then a unsigned right shift.
// To perform a unsigned right shift you need to be clever
integer rightShift(integer value, integer count)
{
return ((value & 0x7fFFffFF) >> count) - ((value & 0x80000000) >> count);
//This works by shifting body and sign bits separately and then
//recombining them. Since the sign bit is extended as a negative
//number, we just need to make it positive. Since the left and
//right values have no bits in common (due to the masking), we
//can use | or + and they will do the same thing. To simplify the
//code we merge the negation and combine into a single operation.
}
// Jonhboy Resident
integer rightShiftJonhboy(integer value, integer count) {
if(count & 31)
return ~(0x80000000 >> (count - 1)) & (value >> count);
return value;
//This works by conditionally applying a calculated mask after
//shifting. The disadvantage of this method is that it is hard
//to inline due to the condition, not to mention the cost of forking.
}
Example Usage:
default
{
state_entry()
{
// output should be 268435449
llSay(DEBUG_CHANNEL, (string)rightShift(-99, 4)); // output: 268435449
llSay(DEBUG_CHANNEL, (string)rightShiftJonhboy(-99, 4)); // output: 268435449
// before: 1111 1111 1111 1111 1111 1111 1001 1101
// after: 0000 1111 1111 1111 1111 1111 1111 1001
}
}