Garry's Mod Wiki

Bitwise Guide

Bitwise Explanation

Hopefully as you know, everything in computers in made of binary at its lowest level. Every 1 and 0 in binary is called a bit, and every 8 bits is called a byte. This structure forms everything on the computer representing character and numbers to more complex operations to advanced to cover here.

Bitwise Operations are functions designed to manipulate these bits. This can be used for various reason but commonly its used to manually flip the binary 1's and 0's in order for the programmer to store new information in the same amount of space. You can see these functions here in the bit library.

Explanation

Now that you have some fundamental understanding of what bitwise operations lets walk through and example to reinforce your understanding.

The following is a code snippet that "packs" a RGBA value into a 32 bit long int value (represented by Hexadecimal form).

-- These are forward declarations while useful, are not needed for bitwise operations. local band = bit.band local bor = bit.bor local lshift = bit.lshift local rshift = bit.rshift -- Packing the RGBA value local function rgb_to_hex(r,g,b, a) a = a or 255 return bor(lshift(r, 24), lshift(g, 16), lshift(b, 8), a) end -- Unpacking the RGBA value local function hex_to_rgb(hex) return rshift(hex, 24), rshift(band(hex, 0x00ff0000), 16), rshift(band(hex, 0x0000ff00), 8), band(hex, 0x000000ff) end

Now lets break this down operation by operation so you can understand how we use bitwise operations to manually flip these bits.

Packing Process

before we begin lets first visualize the length of binary that we wanna return. because we are wanting to return a number, we will want to return 32 bits:

00000000 00000000 00000000 00000000

Each space represent the 8 bits that make up 1 byte.

For this example our r value will be 255 (in binary its 11111111) lets add this to our int from step 1:

00000000 00000000 00000000 00000000 --Empty Int 00000000 00000000 00000000 11111111 -- 255

now in order to add more bits to this int we will first need to move our value of 255 out of the way, we do this using the bit.lshift function which will take the value we wanna use (255 or 11111111) and move it to the left but n amount of bits.

since we are wanting to move our value to the beginning byte we will need to move it left by 24 bits

00000000 00000000 00000000 00000000 --Empty Int 00000000 00000000 00000000 11111111 -- 255 bit.lshift(255,24) 11111111 00000000 00000000 00000000

we will do this operation for every subsequent RGBA component:

r = 255 or 11111111 g = 200 or 11001000 b = 150 or 10010110 a - 255 or 11111111 --Pseudo Code 00000000 00000000 00000000 00000000 --Empty Int bit.lshift(r,24) -- Adding the r value of 255 and moving it left 24 bits 11111111 00000000 00000000 00000000 bit.lshift(g,16) -- Adding the g value of 200 and moving it left 16 bits 11111111 11001000 00000000 00000000 bit.lshift(b,8) -- Adding the b value of 150 and moving it left 8 bits 11111111 11001000 10010110 00000000

Now because we only have the last 8 bits (or byte) left we can just add our a value to the end with our final bitwise operation.

  • The bit.bor operation only exists here to encompass everything as a bitwise number, it servers no logical benefit TO THIS SNIPPET SPECIFCALLY.
-- Adding our A value of 255 which naturally takes place at the end bit.bor(bit.lshift(r, 24), bit.lshift(g, 16), bit.lshift(b, 8), a) 11111111 11001000 10010110 11111111 -- Note: The binary sequence we just put together can still be represented in decimal or hexadecimal form like any other number 4291335935 -- Decimal form FFC896FF -- Hexadecimal

And that's it for the packing process, what we just completed was the process of taking 4 numerical values and representing them using 1 larger value. Now for the

The Why

Its mainly to save on storage, this can easily be seen if we just compare the actual bits of information the computer sees:

-- If our goal is to store our 4 numbers (255, 200, 150, 255) Here is how the computer would normally store them 11111111 00000000 00000000 00000000 00000000 11001000 00000000 00000000 00000000 00000000 10010110 00000000 00000000 00000000 00000000 11111111 -- You see all the extra bits each number takes just to be represented properly... -- What we accomplished using our bitwise functions was turn all those numbers into: 11111111 11001000 10010110 11111111 -- Now as you can see we are not wasting extra bits of information

Unpacking Process

Now that you know what we are doing the unpacking process is a lot easier. we just simply need to extract our information back out.

All we have to do to is isolate the values out that we wanna extract, we can do this by using the hexadecimal values

0x00ff0000 == 11111111 00000000 00000000 0x0000ff00 == 11111111 00000000 0x000000ff == 11111111 -- to extract the our first byte from: 11111111 11001000 10010110 11111111 we will need to align the bits so our comparison works so we will need to shift our hex by 24 bytes to the right