DEV Community

Cover image for 101 Understanding masking and shifting
theyoz
theyoz

Posted on • Edited on

101 Understanding masking and shifting

I have interviewed countless IT professionals and am stunned that bits and bytes are foreign words to most of them. I have been with IT for a long time; since we had nothing but assemblers. In today's world, where the costs to buy 16 GB RAM is $50, going to that level may be irrelevant for application servers... but in the world of protocols and IoT, it is very important.

Primitives

Numerical values can be signed or unsigned. Signed means they have minus values, unsigned means they don't. Java defines 4 types of integer, all signed.

byte - 8-bit integer from -128 to 127 (1 byte of storage)
short - 16-bit integer from -32,768 to 32,767 (2 bytes of storage)
int - 32-bit integer from -2,147,483,648 to 2,147,483,647 (4 bytes of storage)
long - 64-bit integer from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (8 bytes of storage)

Importantly, you can think of a byte as a storage of 8 independent flags, each of which can be turned on (1) or off (0).

If we map the byte (8 bits) in memory, the following is the integer value 0.

Bits ==> 7 6 5 4 3 2 1 0
Values (0 or 1) ==> 0 0 0 0 0 0 0 0
Integer Values When 1 ==> 128 (27) 64 (26) 32 (25) 16 (24) 8 (23) 4 (22) 2 (21) 1 (20)

As you can see, all combinations between 0 and 255 are possible if they are unsigned. When you form a number, you have to go from the highest bit (most significant) to the lowest (least significant). That is, from bit 7 to bit 0. Take 33, for example.

Bit 7? No 128 is higher than 33.
Bit 6? No 64 is higher than 33.
Bit 5? Yes 32 is lower than 33. When you minus 32, we're left with 1 which is the least significant bit of the byte.

Bits 7 6 5 4 3 2 1 0
Values 0 0 1 0 0 0 0 1

Example for 240

Bits 7 6 5 4 3 2 1 0
Values 1 1 1 1 0 0 0 0

Bit Mask

This is all very well, but the aim is to use each bit independently. For example, suppose we want to map a code on a byte (8-bit) that represents a record for a fabric shop:

3 Cloth categories

Casual wear (1)
Formal wear (2)
Sports wear (3)

3 Colors

Blue (1)
White (2)
Black (3)

4 Sizes

Small (1)
Medium (2)
Large (3)
XL (4)

Enter fullscreen mode Exit fullscreen mode

Cloth categories can be kept on 2 bit, since with 2 bit you can make the number 1 (01), 2 (10) or 3 (11). The same can be said for colors. To store the sizes 2 bit is not enough, you need at least 3: 1 (001), 2 (010), 3 (011) and 4 (100).

Let's now map our byte to store all this information.

Bits ==> 7 6 5 4 3 2 1 0
Values _UNUSED_ Size Color Cloth category

So now let's code a casual wear (01), black (11) and size XL (100) => This gives a value of 77 (see map below)

7 6 5 4 3 2 1 0
0 1 0 0 1 1 0 1

How to get the bits in Java

Now that we have a number, we need to look at it as bit map. How do we isolate the bit 0 and 1 (category), bit 2 and 3 (color) and bit 4, 5 and 6 (size)? We can do it my masking and shifting. First, we mask to retrieve the bits in the byte, using the & operator. The mask is all of the bits, that we need from the byte, set to 1.

// Masking the category
int category = mask & 3
Enter fullscreen mode Exit fullscreen mode
7 6 5 4 3 2 1 0
category 0 1 0 0 1 1 0 1
mask 0 0 0 0 0 0 1 1
// Masking the color
int color = mask & 12
Enter fullscreen mode Exit fullscreen mode
7 6 5 4 3 2 1 0
category 0 1 0 0 1 1 0 1
mask 0 0 0 0 1 1 0 0
// Masking the size
int size = mask & 112
Enter fullscreen mode Exit fullscreen mode
7 6 5 4 3 2 1 0
category 0 1 0 0 1 1 0 1
mask 0 1 1 1 0 0 0 0

Then we shift the bits to the right to get the value we want, using the >> (shift right) operator.

// Masking the category
int category = (mask & 3) 
Enter fullscreen mode Exit fullscreen mode

No need to shift category, it's already on the least significant part of the byte.

// Masking the color
int color = (mask & 12) >> 2
Enter fullscreen mode Exit fullscreen mode
7 6 5 4 3 2 1 0
Color 0 0 0 0 ==> 2 1 1
// Masking the size
int size = (mask & 112) >> 4
Enter fullscreen mode Exit fullscreen mode
7 6 5 4 3 2 1 0
Size 0 ==> 4 1 0 0

In code

package ans.examples;

public class BitsAndBytes {
    public static final int WEAR_CASUAL = 1;
    public static final int WEAR_FORMAL = 2;
    public static final int WEAR_SPORT = 3;

    public static final int COLOR_BLUE = 1;
    public static final int COLOR_WHITE = 2;
    public static final int COLOR_BLACK = 3;

    public static final int SIZE_SMALL = 1;
    public static final int SIZE_MEDIUM = 2;
    public static final int SIZE_LARGE = 3;
    public static final int SIZE_XL = 4;

    public static byte makeCode(int wear, int color, int size) {
        return (byte)(wear + (color << 2) + (size << 4));
    }

    public static int getWear(byte code) {
        return code & 3;
    }

    public static int getColor(byte code) {
        return (code & 12) >> 2;
    }

    public static int getSize(byte code) {
        return (code & 112) >> 4;
    }

    public static void main(String[] args) {
        byte code = makeCode(WEAR_CASUAL, COLOR_BLACK, SIZE_XL);
        System.out.println("Causal Wear, Black, Size XL = " + code);

        switch (getWear(code)) {
            case WEAR_CASUAL:
                System.out.println("Casual wear");
                break;

            case WEAR_FORMAL:
                System.out.println("Formal wear");
                break;

            case WEAR_SPORT:
                System.out.println("Sport wear");
                break;

            default:
                System.out.println("Invalid wear code");
                break;
        }

        switch (getColor(code)) {
            case COLOR_BLUE:
                System.out.println("Color Blue");
                break;

            case COLOR_WHITE:
                System.out.println("Color White");
                break;

            case COLOR_BLACK:
                System.out.println("Color Black");
                break;

            default:
                System.out.println("Invalid color code");
                break;
        }

        switch (getSize(code)) {
            case SIZE_SMALL:
                System.out.println("Size Small");
                break;

            case SIZE_MEDIUM:
                System.out.println("Size Medium");
                break;

            case SIZE_LARGE:
                System.out.println("Size large");
                break;

            case SIZE_XL:
                System.out.println("Size XL");
                break;

            default:
                System.out.println("Invalid size code");
                break;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)