|
Haruki Watanabe
llSLCrash(void);
Join date: 28 Mar 2007
Posts: 434
|
12-25-2007 05:05
Trying to get my head around this bitshifting stuff... you know like (alpha * 255.0) & 0xFF << 24 I read some tutorials and I got the idea of bitfields and the OR, AND, XOR stuff... Where I hang is, how to handle the Masks (0xFF) and what they exactly do. I mean, I can see the results, but without really understanding what's going on, I'll never be able to use this stuff properly (man, I don't even WANNA fly to the moon, why do I have to learn ROCKET-SCIENCE?  - The problem hereby is, that I know what the Mask is doing - but when it comes to the HEX-Numbers and how to convert the binary field into a HEX, I totally hang... Are there any good tutorials out there which you guys could recommend? like «bitshifting for dummies»? Thanks & merry x-mas to all of you! [edit] ok - I think I'm one step further  When I use a mask like 0xFF I can use numbers from 0 to 255 (16*16 = 256). now, I do this: num1 = 255; big = num1 << 16; // = 16711680 n1 = big >> 16; // = 255 but when I do this: num1 = 255; big = num1 << 24; // = -16777216 n1 = big >> 24; // = -1 *** num1 = 127; big = num1 << 24; // = 2130706432 n1 = big >> 24; // = 127 Why can I display only numbers <= 127 when I do a bitshift by 24? Sorry for asking such basic questions - guess, a little class in Computer Math would be helpful 
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
12-25-2007 06:00
Ok, a single hex character can exactly represent 4 bits. The following table shows equivalent hex, binary, and decimal values:
0x0 0000 0 0x1 0001 1 0x2 0010 2 0x3 0011 3 0x4 0100 4 0x5 0101 5 0x6 0110 6 0x7 0111 7 0x8 1000 8 0x9 1001 9 0xA 1010 10 0xB 1011 11 0xC 1100 12 0xD 1101 13 0xE 1110 14 0xF 1111 15
So looking again at your example, the section in parentheses takes (what's *expected* to be) a 0.0 - 1.0 ranged floating-point value, and converts it to a 0 - 255 range integral value. (Well, to be precise, it results in a 0.0 - 255.0 range float, but the next step essentially casts this to an integer.)
Then the value 0xFF is being used as a mask, which is equivalent to the binary value:
0000 0000 0000 0000 0000 0000 1111 1111
(As with decimal, leading zeroes in a Hex value can be omitted. But when representing in binary, especially when dealing with an integer as a bitfield rather than a numeric value, it's helpful to see the whole 32-bit value. The spaces between blocks of 4 bits are just easy notation for converting form binary to hex and back.)
When this mask is ANDed with another integer, it's basically saying "Keep the last 8 bits and make the rest zero."
Then the last part simply shift those bits 24 places to the left.
So lets take a concrete example. Suppose alpha is 0.5:
0.5 * 255.0 = 127.5
The next step casts to an integer, so everything after the decimal is discarded. 127 in binary is:
0000 0000 0000 0000 0000 0000 0111 1111
This is then ANDed with 0xFF, so the first 24 bits are zeroed, the rest are left as they are. This is relevant in cases where Alpha might be greater than 1, in which case the value simply "loops" back to zero.
Then the bits are shifted 24 places to the left, so the resulting value is:
0111 1111 0000 0000 0000 0000 0000 0000
Now, you might ask, what is the purpose of the mask, if anything above the 8th bit would have shifted off the end of the buffer anyway? Well, let's take another example:
( G * 255.0 ) & 0xFF << 8
And assume that an "improper" G value greater than 1 was passed to it, say 1.5:
1.5 * 255 = 382.5
Discarding the fractional element and converting to binary, this is:
0000 0000 0000 0000 0000 0001 0111 1110
Now, if this weren't masked and simply shifted, the result would be:
0000 0000 0000 0001 0111 1110 0000 0000
This is relevant because your example was only one part of a formula to mux separate ARGB values into a single 32-bit integer. 8 bits for Alpha, 8 bits for Red, 8 bits for Green, and 8 bits for Blue If this were ORed together with the A, R and B values, the R value would have a 1 in it's first bit regardless of what the original R value was. However, when the mask is applied, that 1 in the 9th bit would be zeroed, resulting in:
0000 0000 0000 0000 0000 0000 0111 1110 (126 in decimal)
Which, when shifted, wouldn't affect the "red" block of bits. So basically, masks keep everything contained in their own separate compartments.
Have I helped, or just confused you more?
|
|
Haruki Watanabe
llSLCrash(void);
Join date: 28 Mar 2007
Posts: 434
|
Wow...
12-25-2007 06:07
Thanks a lot Deanna! Guess I have to print this out and read it thorougly  Great x-mas present!
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
12-25-2007 06:23
From: Haruki Watanabe num1 = 255; big = num1 << 24; // = -16777216 n1 = big >> 24; // = -1
Ok, what's happening here is you're running into an issue with bit-shifting negative numbers. 255: 0000 0000 0000 0000 0000 0000 1111 1111 shifted 24 to the left: 1111 1111 0000 0000 0000 0000 0000 0000 (-16777216) shifted back 24 to the right: 1111 1111 1111 1111 1111 1111 1111 1111 (-1) Right-shifting a negative value, that is, any which has a 1 in the left-most bit, fills the resulting bits on the left with 1s, not 0s. So the result is 24 bits filled with 1s, and the last 8, which was the shifted value, which also happened to be all 1s. For how negative integral values are represented, see  , which explains it better than I can (mostly because I've never bothered to study Two's Compliment in any great depth) Masking is helpful here too. If you are only concerned with a particular bit field, which might possibly have become "negative" when shifted, mask it: num1 = 255; big = num1 << 24; // = -16777216 n1 = ( big >> 24 ) & 0xFF; // = 255
|
|
Haruki Watanabe
llSLCrash(void);
Join date: 28 Mar 2007
Posts: 434
|
12-25-2007 06:47
You are my hero!  Guess I have to read and try this stuff some more to really get it into my head - but you sure helped me a lot! /me bows
|
|
RJ Source
Green Sky Labs
Join date: 10 Jan 2007
Posts: 272
|
12-25-2007 07:01
There are a lot of links on the web that will do base conversions so you can see what is happening. Here's one: https://fusion.gat.com/~penaflor/basecalc.html
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
12-25-2007 07:31
For that matter, Windows Calculator (in Scientific mode) will deal with numbers in Hex, Decimal, Octal or Binary, and convert any number entered in one of the above to another. Or, here's a few LSL functions to convert an integer to a binary string, which themselves use bit-shifting and masks. This one successively shifts a one-bit mask right-to-left, and tests if the masked bit is 1 or 0 string Int2Bin( integer i ) { integer mask = 1; string out;
while( mask ) { if( in & mask ) out = "1" + out; else out = "0" + out;
mask = mask << 1; }
return out; }
And this one shifts the input successively fewer places to the right and appends the masked right-most bit, cast to a string, to the output: string Int2Bin( integer i ) { integer shift = 32; string out;
while( shift ) out += (string)( ( i >> --shift ) & 1 );
return out; }
Converting an integer to a hex string is a matter of shifting and masking each successive block of 4 bits and using that value as a lookup index in a string containing the 16 hex characters. Converting a hex string to integer is very simple, just prepend "0x" to it and cast to integer. 
|