Bits, Bytes, Binary, Hexa, etc.

I am currently working with some low level code optimized for low-space Arduino micro controller, I needed to review some basics of bits manipulation.


Bits bytes and nibbles, binary and hexa

  • 1 Byte is composed of 8 bits, or (2 nibbles of 4 bits):    0102 0120  1201 0201
  • A WORD is 2 BYTEs, and a DWORD is 4 BYTES or 32 bits.
  • Note: a Dword is an unsigned 32 bits integer 
To simplify binary notations, it is common to use hexadecimal numbers (base 16).

  • It takes 4 bits to cover one digit in Hexadecimal  (from 0 to 9 and A to F). 
  • That is why we use a two digit Hexa number to represent a Byte of data.

LSB and MSB

Imagine the following Byte 1001 0010 0100 1000

The first bit starting from the left (1) is the MSB (Most Significant Bit) as it is the bit with the highest weight on the number

The leftmost digit "0", is the LSB (Least significant bit)


Decimal to Binary conversion:

One method:
- find the power of 2 number which is the closest (inferior) of the decimal number.
- do the same with the rest (rest of the subtraction between the decimal number and this power of 2)

Ex: decimal 52
closest power of 2 number : 32, (2^4)
rest: 52 - 32 = 20
closest power of 2 of 20 : 16 (2^3)
rest: 20 - 16 = 4
closest power of 2 : 4 (2^2)

So binary number is:   11100

Binary to decimal conversion

11100 
Sum the power of 2 of each bit with value of "1", being careful as the first digit is at position "zero"
ex: 
   2^4   =  32      (position 4 and not 5 as we start with 0)
+ 2^3   =  16
+ 2^2   =    4
-----------------
                 52

Bitwise operators

& : AND operator 
(logical AND) Compares two values only if they have their bit sets.

|  : OR operator
(logical OR)

^ : XOR operator, used to toggle bits

~ : NOT operator (inverts bits values)


Using | to set flags:

For exemple, imagine you have different binary flags defined for different parameters:
enum{
style1  = 1,
style2  = 2,
style3 = 4,
...
};

equivalent to:

Style1 = 0b 00000001
Style2 = 0b 00000010
Style3 = 0b 00000100

To set both style 1 and style 3, we use the bit "or": | which is similar as addition
style1 | style3 == 0b 00000101

Using & to check wether flags are set:

How do we test if the flag of "style3" is set in the DWORD passed in parameter:

DWORD parameter:     00000000 00000000 00000000 00100101
Style3 MASK          00000000 00000000 00000000 00000100 
bitwise AND (&)  00000000 00000000 00000000 00000100  bit set

other DWORD parameter:  01100001 01100100 10000001 10100000 
Style3 MASK:            00000000 00000000 00000000 00000100 
bitwise AND (&)     00000000 00000000 00000000 00000000 bit !set

if the bit and (&) operation returns 0, the bit is not set, if it returns 1 it is!

Using ~ to remove a flag setting:

newStyle =  oldStyle & ~style3;   (newStyle = oldStyle AND NOT style3)


>> << Right and Left shift operators

>> or << operators (right shift / left shift operators)
Move the bits the number of bit position specified:
>> moves the bits from the higher bit to the lower bit
<< idem from the lower bit to the high bit 

These operators are frequently used in enum data structures;

In C enum structures are used to store a list of predefined constants, these constants often use a power of 2, as it is easier to combine them using bit operators.
see here for more details.

Using bit is useful to use the least amount of memory as possible:
ex: date with least amount of memory

Struct date_struct{
  BYTE day : 5,  //1 to 31
       month: 4, //1 to 12
       year : 14; // 0 to 9999
} date;

in this exemple the day field takes the lowest 5 bits, month the next 4, and year the next 14, so we can store the date structure in 23 bits, contained in 3 BYTEs.
If we chose an INT for each field, the structure would have taken  12 BYTEs.

| 00000000 0000000 | 0000 | 00000 |
   year                          month  day 

How does it work: a BYTE is 8 bits, by using it the compiler will allocate 1 BYTE for storage. If we use more than 8 bits in our structure, the compile will allocate another BYTE, as many as it takes to hold our structure. 

variable : 8 (8 bits used)

Thanks to this structure notation, we can use the address of the structure.
date.day = 12;
dateauj = &date;
(note &date is the memory location of variable date, see here for more on pointers
dateauj -> year = 1852;


That's all for tonight! 









Commentaires