Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Actionable bitwise with C++
Bosley
Bosley

Posted on

     

Actionable bitwise with C++

I've seen a few posts here and there talking about bitwise operations. I enjoyed reading them, but a lot of the best ones (imo) didn't show great examples of bitwise usage. As someone who leverages bitwise operations everyday at my job, I figured I'd make a post to potentially offer some clarity on the subject by writing up a big example.

Before we get to examples of where we might use this stuff, lets look over the bitwise operations.

If you'd like to see executable code with all of this information, check it outhere.

AND

AND-ing two binary values will indicate when corresponding bits in the two inputs are '1' or in the 'on' position.

For example :

    00000101 & 00000011 = 00000001
Enter fullscreen modeExit fullscreen mode

Here is the same thing, but stacked to give an easier visual representation of what is going on:

    00000101    00000011    --------    00000001
Enter fullscreen modeExit fullscreen mode

OR

OR-ing two binary values will yield a result that tells us if a bit in one input OR a bit in the other input is '1' or in the 'on' position.

For example :

    01001000 | 10111000 = 11111000
Enter fullscreen modeExit fullscreen mode

Here is the same thing, but stacked to give an easier visual representation of what is going on:

    01001000    10111000    --------    11111000
Enter fullscreen modeExit fullscreen mode

XOR

XOR-ing is pretty cool. Similar to OR, but different in that it requires exclusivity in the input bits being in the '1' or 'on' position. Easily understood as "One or the other, but not both."

    11111111 ^ 00001111 = 11110000
Enter fullscreen modeExit fullscreen mode

Here is the same thing, but stacked to give an easier visual representation of what is going on:

    11111111    00001111    --------    11110000
Enter fullscreen modeExit fullscreen mode

NEG

Negation! This can be understood as 'flipping' or 'toggling' the bits.

    ~11111010 = 00000101
Enter fullscreen modeExit fullscreen mode

Again, the stacked example:

    11111010    --------    00000101
Enter fullscreen modeExit fullscreen mode

Shifting

The last thing we will look at before the big example is shifting. Shifting is neat. We basically just push the bits left or right within the binary number.

    0x01      = 00000001    0x01 << 1 = 00000010    0x01 << 2 = 00000100    0x01 << 3 = 00001000
Enter fullscreen modeExit fullscreen mode

Of course, we can also shift in the other direction!

10000000 >> 5 = 00000100
Enter fullscreen modeExit fullscreen mode

As you can see, by >> by '5' we've moved the bit 5 spaces to the right. Wicked!

Time for some use-cases (examples)!

These are some cases where we might chose to use bitwise logic.

MSB Checking

This one might seem strange, but there are cases where we might want to see if the far-left bit of a byte is in the 'on' position.

voidsomeFunc(){uint8_tvar=0x8A;// In binary, this is : 10001010// If we want to see if the most significant bit is set// we can right shift a few places and use it as a bool// because 1's are true, and 0's are false.boolisBitSet=var>>7;if(isBitSet){// Of course, we COULD drop ' var >> 7 ' in as the conditional// directly, but for the example I decided not to.std::cout<<"The bit is set!"<<std::endl;}}
Enter fullscreen modeExit fullscreen mode

Masking

Lets say we have a 32-bit number that represents a color! We all like colors. Why 32-bit ? Because it can happen. Lets say our color happens to be encoded as-follows:

    Alpha = Byte One    Blue  = Byte Two    Green = Byte Three    Red   = Byte Four
Enter fullscreen modeExit fullscreen mode

Given the following 32-bit binary number, how can we extract these values?

    Alpha    Blue     Green    Red    10001100 11101001 00001010 00000011
Enter fullscreen modeExit fullscreen mode

MASKS

We'll use the following masks:

uint32_talphaMask=0xFF000000;// Mask to obtain alpha byteuint32_tblueMask=0x00FF0000;// Mask to obtain blue byteuint32_tgreenmask=0x0000FF00;// Mask to obtain green byteuint32_tredMask=0x000000FF;// Mask to obtain red byte
Enter fullscreen modeExit fullscreen mode

Using these masks, we can leverage the functionality of AND to ensure that we get the value we want... and that is pretty great.

//                     A B G Ruint32_tabgrColor=0x8CE90A03;// The actual 32-bit coloruint32_talpha=abgrColor&alphaMask;uint32_tblue=abgrColor&blueMask;uint32_tgreen=abgrColor&greenmask;uint32_tred=abgrColor&redMask;}
Enter fullscreen modeExit fullscreen mode

To demonstrate one of these as we did above, lets take the binary representations of abgr and stack it on the blue mask.

10001100 11101001 00001010 0000001100000000 11111111 00000000 00000000----------------------------------------00000000 11101001 00000000 00000000
Enter fullscreen modeExit fullscreen mode

Hooray! We've discovered that the byte representing our blue color is :

    11101001
Enter fullscreen modeExit fullscreen mode

Chopping!

Lets say we have 32 bits ( a color maybe? ) and we want to send it somewhere. The specification for the protocol that we need to send it over demands we do it 8 bits at a time. Why? I don't know, I didn't write the spec.

In order to do this we need to chop the 32 bits up into 4 bytes and send them sequentially. Then, on the other side, we need to reconstruct the original data.

Its okay, we can do this.

uint32_tvar=0x8CE90A03;// This is our color, but in hexuint8_tpack[4];// A 'pack' of 4 bytes// Using a mask (like above) we grab the datapack[0]=(var&0x000000FF);pack[1]=(var&0x0000FF00)>>8;// But when we mask some bits wepack[2]=(var&0x00FF0000)>>16;// need to move them into a rangepack[3]=(var&0xFF000000)>>24;// that works within a byte
Enter fullscreen modeExit fullscreen mode

Explanation

If the masking and shifting seems confusing maybe this will help!
If you recall from the blue byte extraction above we ended up with the following data:

    Byte 1   Byte 2   Byte 3   Byte 4    00000000 11101001 00000000 00000000
Enter fullscreen modeExit fullscreen mode

However, uint8_t represents 1 byte. If we attempted to construct a uint8_t with that data, it would be 00000000 as only 'Byte 4' would be used. That means we need to get JUST the data in 'Byte 2' we have to right shift by 16.

      00000000 11101001 00000000 00000000 >> 16       -----------------------------------      00000000 00000000 00000000 11101001
Enter fullscreen modeExit fullscreen mode

Woot! Now that '11101001' is in the far right we can safely construct a uint8_t and preserve the 'blue' data.

/Explanation

Now that we have a pack of bytes representing our color data, lets just assume we called something to send it, and now we need to reconstruct it as a uint32_t on the receiver end.

This is relatively easy, but not necessarily straight forward.

uint32_tunpacked=pack[0]|(pack[1]<<8)|(pack[2]<<16)|(pack[3]<<24);
Enter fullscreen modeExit fullscreen mode

We're done!

But what did we do?

We undid all of the chopping of course! We took each byte, and placed them into their corresponding space within the new 32-bit number.

Lets take it step by step.

When we packed the data, masked each byte and put it in the pack. Here is what it looked like:

    initial data = 10001100 11101001 00001010 00000011    pack[0] = 00000011  // red    pack[1] = 00001010  // green    pack[2] = 11101001  // blue    pack[3] = 10001100  // alpha
Enter fullscreen modeExit fullscreen mode

As we reassembled the bytes, we followed these steps:

    // Created a 32-bit variable    00000000 00000000 00000000 00000000    // Added pack[0]    00000000 00000000 00000000 00000011    // Added pack[1] (by or-ing), and shifted it left 8 bits    00000000 00000000 00001010 00000011    // Added pack[2] (by or-ing), and shifted it left 16 bits    00000000 11101001 00001010 00000011    // Added pack[3] (by or-ing), and shifted it left 24 bits    10001100 11101001 00001010 00000011
Enter fullscreen modeExit fullscreen mode

Did it work? Lets check

    Original value: 10001100 11101001 00001010 00000011    Reconstructed : 10001100 11101001 00001010 00000011
Enter fullscreen modeExit fullscreen mode

It sure looks like it worked!

Ending remarks

That was a lot of words for me. I don't usually write things for people, but this bitwise stuff is pretty useful and I enjoy it quite a bit.

If you've made it this far and haven't done so yet, you shouldcheck out the code I wrote to demonstrate everything.

This was my first article here, I hope I made things clear and didn't goof anything up. If you noticed any mistakes, or if anything is unclear please let me know and I will do my best to clarify things and/or fix them.

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
gapry profile image
Gapry
Short-term actions, long-term impact.
  • Joined

Hi, Bosley

I write a blog post about bitwise operation of C/C++ recently. I think you may be interested for it so I want to share with you. Here is the link,Convert the endianness in C++ and test it with GDB and Python

Best regards, Gapry.

CollapseExpand
 
bosley profile image
Bosley
I'm a software developer interested low level programming, real time operating systems, and things of that nature. Huge fan of Rust and C++.
  • Location
    MI
  • Work
    Computer Engineer II
  • Joined

Thank you for sharing!

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm a software developer interested low level programming, real time operating systems, and things of that nature. Huge fan of Rust and C++.
  • Location
    MI
  • Work
    Computer Engineer II
  • Joined

More fromBosley

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp