Applied Computer Programming Representation of Numbers. Bitwise Operators Course 07 Lect.eng. Adriana ALBU, PhD Politehnica University Timisoara
Internal representation All data, of any type, processed by a computer must have the same fundamental representation In order to work correctly with these data, a programmer should understand: the representation of numbers and their memory storage the fundamental manner a computer operates with numbers (bitwise operations)
Representation of numbers Binary Octal Hexadecimal
Representation of numbers Most of our programs don t need concern regarding operations at the bit level We re free to think in bytes, or integers and doubles, or even higher level data types composed of a combination of these But there are times when we'd like to be able to go to the level of an individual bit
Representation of numbers Any value (variable or constant) that is processed by our programs needs a memory space and it has a memory representation (sequence of bits) Bit unit of data storage may have one of two values: 0 or 1 usually is not individually addressable Byte unit of addressable data in all usual computer architectures it contains 8 bits (CHAR_BIT from limits.h specifies its dimension)
Representation of numbers The byte is the lowest level at which we can access data; there's no "bit" type, and we can't ask for an individual bit In fact, we can't even perform operations on a single bit every bitwise operator will be applied to, at a minimum, an entire byte at a time This means we'll be considering the whole representation of a number whenever we talk about applying a bitwise operators
Binary representation Binary numbers used in mathematics and digital electronics expressed in the binary numeral system (also called base-2 numeral system) use two different symbols: 1 (one) and 0 (zero) these two symbols may also have the meanings of true/false, yes/no, on/off the binary system is used internally by almost all modern computers and computer-based devices such as mobile phones
Binary representation Any number can be represented by a sequence of bits (binary digits), which in turn may be represented by any mechanism capable of being in two mutually exclusive states In a computer, the numeric values may be represented by two different voltages
Binary representation When written, binary numerals are often subscripted, prefixed or suffixed in order to indicate their base The following notations are equivalent: 11100101 binary (explicit statement of format) 11100101b (a suffix indicating binary format) 11100101B (a suffix indicating binary format) bin 11100101 (a prefix indicating binary format) 11100101 2 (a subscript indicating base-2 (binary) notation) %11100101 (a prefix indicating binary format) 0b11100101 (a prefix indicating binary format, common in programming languages) 8b11100101 (a prefix indicating number of bits in binary format, common in programming languages)
Binary representation When spoken, binary numerals are usually read digit-by-digit, in order to distinguish them from decimal numerals the binary numeral 100 is pronounced one zero zero, rather than one hundred, to make its binary nature explicit, and for purposes of correctness since the binary numeral 100 represents the value four, it would be confusing to refer to the numeral as one hundred (a word that represents a completely different value, or amount) alternatively, the binary numeral 100 can be read out as "four" (the correct value), but this does not make its binary nature explicit
Counting in binary Counting in binary is similar to counting in any other number system Beginning with a single digit, counting proceeds through each symbol, in increasing order Before examining binary counting, it is useful to briefly discuss the more familiar decimal counting system as a frame of reference
Decimal counting Decimal counting uses the ten symbols 0 through 9 Counting primarily involves incremental manipulation of the "low-order" digit, or the rightmost digit, often called the "first digit When the available symbols for the low-order digit are exhausted, the next-higher-order digit (located one position to the left) is incremented, and counting in the low-order digit starts over at 0
Decimal counting In decimal, counting proceeds like so: 000, 001, 002,... 007, 008, 009, (rightmost digit starts over, and next digit is incremented) 010, 011, 012,...... 090, 091, 092,... 097, 098, 099, (rightmost two digits start over, and next digit is incremented) 100, 101, 102,... After a digit reaches 9, an increment resets it to 0, but also causes an increment of the next digit to the left
Binary counting In binary, counting follows similar procedure, except that only the two symbols 0 and 1 are used Thus, after a digit reaches 1 in binary, an increment resets it to 0, but also causes an increment of the next digit to the left: 0000, 0001, (rightmost digit starts over, and next digit is incremented) 0010, 0011, (rightmost two digits start over, and next digit is incremented) 0100, 0101, 0110, 0111, (rightmost three digits start over, and the next digit is incremented) 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111...
Binary to decimal Since binary is a base-2 system, each digit represents an increasing power of 2, with the rightmost digit representing 2 0, the next representing 2 1, then 2 2, and so on To determine the decimal representation of a binary number simply take the sum of the products of the binary digits and the powers of 2 which they represent For example, the binary number 100101 is converted to decimal form as follows: 100101 2 = 1 2 5 + 0 2 4 + 0 2 3 + 1 2 2 + 0 2 1 + 1 2 0 100101 2 = 1 32 + 0 16 + 0 8 + 1 4 + 0 2 + 1 1 100101 2 = 37 10
Decimal to binary 157 2 156 78 2 1 78 39 2 0 38 19 2 1 18 9 2 1 8 4 2 1 4 2 2 0 2 1 2 0 0 0 1 Successive divisions by 2, storing the reminder (0 or 1) Stop when the quotient is 0 The binary result is the sequence of reminders read from the end to the beginning: => 157 10 = 10011101 2 Validation: 10011101 2 = 1 2 7 + 1 2 4 + 1 2 3 + 1 2 2 + 1 2 0 = 128 + 16 + 8 + 4 + 1 = 157 10
Decimal to other bases This method can be modified to convert from decimal to any base The divisor was 2 because the desired destination was base 2 (binary) If the desired destination is a different base, replace the 2 in the method with the desired base For example, if the desired destination is base 8, replace the 2 with 8 The final result will then be in the desired base
Decimal to octal 870 8 864 108 8 6 104 13 8 4 8 1 8 5 0 0 1 Octal is base-8, with numbers between 0 and 7 =>870 10 = 1546 8 common writing in programming languages: prefixed by zero: 01546 Validation: 1546 8 = 1 8 3 + 5 8 2 + 4 8 1 + 6 8 0 = 512 + 320 + 32 + 6 = 870 10
Decimal to hexadecimal The hexadecimal (base sixteen) numeral system has sixteen possible values, using the letters A, B, C, D, E, and F for the six place-values after 9: Dec 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E F Oct 0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 Bin 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111
Decimal to hexadecimal 19959 16 19952 1247 16 7 1232 77 16 15 64 4 16 (F) 13 0 0 (D) 4 => 19959 10 = 4DF7 16 common writing in programming languages: prefixed by 0x: 0x4DF7 Validation: 4DF7 16 = 4 16 3 + 13 16 2 + 15 16 1 + 7 16 0 = 16384 + 3328 + 240 + 7 = 19959 10
Binary arithmetic Arithmetic in binary is much like arithmetic in other numeral systems addition subtraction multiplication division
Addition The simplest arithmetic operation in binary is addition Adding two single-digit binary numbers is relatively simple, using a form of carrying: 0 + 0 0 0 + 1 1 1 + 0 1 1 + 1 0, carry 1 => 10 2 (1 2 1 +0 2 0 = 2 10 ) Addition table 0 1 0 0 1 1 1 10 Adding two "1" digits produces a digit "0", while 1 will have to be added to the next column
Addition This is similar to what happens in decimal when certain single-digit numbers are added together; if the result equals or exceeds the value of the base (10), the digit to the left is incremented: 5 + 5 0, carry 1 (since 5 + 5 = 10 = 1 10 1 + 0 10 0 ) 7 + 9 6, carry 1 (since 7 + 9 = 16 = 1 10 1 + 6 10 0 ) This is known as carrying When the result of an addition exceeds the value of a digit, the procedure is to "carry" the excess amount divided by the radix (that is, 10/10) to the left, adding it to the next positional value
Addition Carrying works the same way in binary: In this example, two numerals are being added together: 01101 2 (13 10 ) and 10111 2 (23 10 ) The top row shows the carry bits used 1 1 1 1 1 carried digits 0 1 1 0 1 + 1 0 1 1 1 1 0 0 1 0 0 Starting in the rightmost column, 1 + 1 = 10 2 ; the 1 is carried to the left, and the 0 is written at the bottom of the rightmost column The second column from the right is added: 1 + 0 + 1 = 10 2 again; the 1 is carried, and 0 is written at the bottom The third column: 1 + 1 + 1 = 11 2 ; this time, a 1 is carried, and a 1 is written in the bottom row Proceeding like this gives the final answer 100100 2 (36 10 )
Subtraction Subtraction works in much the same way: starred columns are borrowed from * * * * 0 0 0 0 1 1, borrow 1 1 1 0 1 1 1 0 1 0 1 1 0 1 1 1 1 1 0 1 0 1 0 1 1 1 Subtracting a "1" digit from a "0" digit produces the digit "1", while 1 will have to be subtracted from the next column This is known as borrowing The principle is the same as for carrying; when the result of a subtraction is less than 0, the least possible value of a digit, the procedure is to "borrow" the deficit divided by the radix from the left, subtracting it from the next positional value
Multiplication Multiplication in binary is similar to its decimal counterpart Two numbers A and B can be multiplied by partial products: for each digit in B, the product of that digit in A is calculated and written on a new line, shifted leftward so that its rightmost digit lines up with the digit in B that was used The sum of all these partial products gives the final result
Multiplication Since there are only two digits in binary, there are only two possible outcomes of each partial multiplication: if the digit in B is 0, the partial product is also 0 if the digit in B is 1, the partial product is equal to A Multiplication table 0 1 0 0 0 1 0 1
Multiplication For example, the binary numbers 1110 and 1010 are multiplied as follows: 1110 2 =14 10 1010 2 =10 10 10001100 2 =140 10 1 1 1 0 1 0 1 0 0 0 0 0 + 1 1 1 0 + 0 0 0 0 + 1 1 1 0 1 0 0 0 1 1 0 0
Division Binary division is again similar to its decimal counterpart: Here, the divisor is 101 2, or 5 decimal, while the dividend is 11011 2, or 27 decimal The procedure is: the divisor 101 2 goes into the first three digits 110 2 of the dividend one time, so a "1" is written on the result line this result is multiplied by the divisor, and subtracted from the first three digits of the dividend; the next digit (a "1") is included to obtain a new three-digit sequence the procedure is then repeated with the new sequence, continuing until the digits in the dividend have been exhausted
Division Thus, the quotient of 11011 2 divided by 101 2 is 101 2, as shown on the result line, while the remainder, shown on the bottom line, is 10 2 In decimal, 27 divided by 5 is 5, with a remainder of 2 1 1 0 1 1 1 0 1 1 0 1 1 0 1 0 1 1 0 0 0 1 1 1 1 0 1 1 0
The sizeof operator Before operating with bits, an operator that determines the number of bytes used in a data type representation is needed
The sizeof operator It is an unary operator used to determine the size of a data type, measured in number of bytes required to represent that type It can be applied to any data type (standard or userdefined)
The sizeof operator The keyword "sizeof" is followed by a type name or an expression (which may merely be a variable name) If a type name is used, it must always be enclosed in parentheses, whereas expressions can be specified with or without parentheses with data types, sizeof evaluates the size of the memory representation for an object of the specified data type for expressions it evaluates the representation size for the type that would result from evaluation of the expression (which however is not evaluated)
Why do we need sizeof? To write portable programs sizes of types are implementation dependent (processor, operating system, compiler) the dimension of a data type (even it is a standard data type) is not precisely defined by the standard (for instance, an integer can be represented on 2 or on 4 bytes); the only data type that has the same representation (using 1 byte) in any standard C implementation is char for example, don t write programs that assumes an integer has 2 bytes; it is possible to run incorrectly on other systems
Why do we need sizeof? It is frequently very difficult to predict the sizes of compound data types such as a struct or a union In bitwise operations and in dynamic memory allocation it is required to know how many bytes a variable or a data type has
Example 1 a C program since sizeof(char) is defined to be 1 and assuming that ints are 4 bytes long and doubles are 8 bytes long, the following code will print: 1, 8, 4: #include <stdio.h> void main(void){ int a; double b; char c; printf("%d, %d, %d", sizeof c, sizeof (a+b), sizeof(int)); }
Bitwise operations NOT, AND, OR, XOR, SHIFT
Bitwise operations Though not directly related to the numerical interpretation of binary symbols, sequences of bits may be manipulated using Boolean logical operators When a sequence of binary symbols is manipulated this way, it is called a bitwise operation AND, OR, and XOR may be performed on corresponding bits in two binary numerals provided as input NOT operation may be performed on individual bits in a single binary numeral provided as input arithmetic shifts other important operations (used, for instance, in multiplication or division by two)
Bitwise operations in C Operations on bits at individual levels can be carried out using Bitwise operations in the C programming language Bits come together to form a byte which is the lowest form of data that can be accessed in digital hardware The whole representation of a number is considered while applying a bitwise operator
Bitwise operators in C They can be used only for integer operands C programming language provides six operators for bit manipulation: Symbol Operator ~ bitwise NOT (unary operator) & bitwise AND bitwise OR ^ bitwise exclusive OR (XOR) >> right shift << left shift
Bitwise NOT ~ It represents the complement of a given number for every bit 1, the result is 0 for every bit 0, the result is 1 a ~ a 0 1 1 0 ~10100011 = 01011100
Bitwise AND & It is just a representation of AND and does its work on bits and not on bytes, chars, integers, etc. So basically a binary AND performs the logical AND on the bits in each position of a number in its binary form 11001110 10011000 10001000 & the most significant bit of the first number is 1 and that of the second number is also 1, so the most significant bit of the result must be 1 in the second most significant bit, the bit of second number is zero, so the result is 0 a b a & b 0 0 0 0 1 0 1 0 0 1 1 1
Bitwise OR Similar to Bitwise AND, Bitwise OR only operates on bits, not bytes Its result is a 1 if one of the either bits is 1 and zero only when both bits are 0 11001110 10011000 11011110 a b a b 0 0 0 0 1 1 1 0 1 1 1 1
Bitwise XOR ^ The Bitwise XOR (exclusive OR) adds two bits while discarding the carry The result is zero only when 2 zeroes or ones are involved Sometimes XOR might just be used to toggle the bits between 1 and 0 Thus: i=i^1 when used in a loop toggles its values between 1 and 0 a b a ^ b 0 0 0 0 1 1 1 0 1 1 1 0
Bitwise XOR ^ You can think of XOR in the following way: you have some bit, either 1 or 0, that we'll call A when you take A XOR 0, then you always get A back: if A is 1, you get 1, and if A is 0, you get 0 on the other hand, when you take A XOR 1, you flip A: if A is 0, you get 1; if A is 1, you get 0 So you can think of the XOR operation as a sort of selective twiddle: if you apply XOR to two numbers, one of which is all 1s, you get the equivalent of a twiddle
Right shift >> It requires two operands It shifts each bit in its left operand to the right The number following the operator (the right operand) decides the number of places the bits are shifted Thus by doing ch>>3 all the bits will be shifted to the right by three places
Right shift >> Example: If the variable ch contains the bit pattern 11100101, then ch >> 1 will give the output as 01110010, and ch >> 2 will give 00111001 If the number is unsigned, zeros are generated on the left when the bits are shifted to the right Else implementation dependent behavior (usually repeats the sign bit the first bit of the number) => right shift only unsigned numbers (for portability)
Right shift >> Right shift can be used to divide a bit pattern by 2 as shown: i=14; // bit pattern 1110 j=i>>1; /* the bit pattern is shifted by 1, obtaining 0111=7 which is 14/2 */
Example 2 a C program A program that displays the binary representation of a number using the right shift operator #include <stdio.h> #include <limits.h> void main(void){ unsigned int n; int i; printf("n="); scanf("%u",&n); printf("%u = ", n); for(i=sizeof(unsigned int)*char_bit-1;i>=0;i--) printf("%u",(n>>i)&1); }
Left shift << It shifts each bit in its left operand to the left It works opposite to that of right shift operator Thus by doing ch<<1 on a variable ch that contains the bit pattern 01100101, the result will be 11001010 Blank spaces generated are filled up by zeros
Left shift << Left shift can be used to multiply an integer by 2 as in: int i=4; /* bit pattern is 100 */ int j=i<<1; /* => 1000, which is 8 */
Example 3 a C program A program that displays the binary representation of a number using the left shift operator #include <stdio.h> #include <limits.h> void main(void){ int i, n; printf("n="); scanf("%d",&n); printf("%d = ", n); for(i=sizeof(int)*char_bit-1;i>=0;i--) if((n&(1<<i))!=0) putch('1'); else putch('0'); }
Bitwise assignment operators C provides compound assignment operators perform the binary operation store the result in the left operand Symbol Operator &= bitwise AND assignment = bitwise OR assignment ^= bitwise XOR assignment >>= right shift assignment <<= left shift assignment
The operators precedence ++,, (type), ~ *, /, % +, <<, >> <, <=, >, >= ==,!= & ^ &&?: =, +=, -=, *=, /=, %=, &=, ^=, =, <<=, >>=
Some applications of bitwise operators One and zero only for a specific bit k 1<<k means 1 only for bit k e.g. 1 for bit 3 1<<3 = 00000001<<3 = 00001000 ~ (1<<k) means 0 only for bit k e.g. 0 for bit 4 ~ (1<<4) = ~ (00000001<<4) = ~ 00010000 = 11101111
Some applications of bitwise operators Get a specific bit k of a number n (verify if it is 0 or 1): n & (1<<k) e.g. bit 2 of number 10010110: 10010110 & (1<<2) = 10010110 & 00000100 = 00000100!= 0 the value given by a sequence of bits of a number n: e.g. bits 3, 2, 1, 0 of number 01011110: 01011110 & 0xF = 01011110 & 00001111 = 00001110 = 14 10
Some applications of bitwise operators Set / Reset set (to 1) a specific bit k of a number n: n (1<<k) OR with 1 is always 1 e.g. set the bit 3 of number 01010011 01010011 (1<<3) = 01010011 00001000 = 01011011 => OR with 0 preserves a bit reset (make 0) a specific bit k of a number n: n & ~ (1<<k) AND with 0 is always 0 e.g. reset the bit 5 of number 11110101 11110101 & ~ (1<<5) = 11110101 & ~ 00100000 = = 11110101 & 11011111 = 11010101 => AND with 1 preserves a bit
Some applications of bitwise operators Flip XOR with 0 preserves a bit XOR with 1 flips a bit flip (change from 1 to 0 and from 0 to 1) a specific bit k of a number n: n ^ (1<<k) e.g. flip the bit 3 of number 10101001 10101001 ^ (1<<3) = 10101001 ^ 00001000 = 10100001 flip it again 10100001 ^ (1<<3) = 10100001 ^ 00001000 = 10101001
Some applications of bitwise operators Example of other bit patterns (also called masks the blue elements in the previous examples) 11110000: ~ 0 << 4 00000011: ~ (~ 0 << 2) 00111110: ~ (~ 0 << 5) << 1 n & (~ (~ 0 << 2)) resets all bits of n, except last 2 bits n & (~ (~ 0 << 5) << 1) resets all bits of n, except 5 bits starting from 1
Example 4 a C program A program that works with a set of 8 LEDs (lightemitting diodes) stored as an unsigned char It offers functions that: verify if a given led is on or off; turn on a specific led; turn off a led; flip a led. All LEDs are initially turned off
#include <stdio.h> #include <limits.h> void display(unsigned char leds){ int i; for(i=char_bit-1;i>=0;i--) printf("%d",(leds>>i)&1); printf("\n"); } int isturnedon(unsigned char leds, int lednumber){ return leds & (1<<ledNumber); } void turnon(unsigned char *leds, int lednumber){ *leds = *leds (1<<ledNumber); } void turnoff(unsigned char *leds, int lednumber){ *leds = *leds & ~(1<<ledNumber); } void flip(unsigned char *leds, int lednumber){ *leds = *leds ^ (1<<ledNumber); } void main(){ unsigned char leds=0; int i; display(leds); turnon(&leds,5); display(leds); turnoff(&leds,5); display(leds); if(!isturnedon(leds,4)) turnon(&leds,4); display(leds); for(i=1;i<10;i++){ flip(&leds,0); display(leds); } }
Example 5 a C program A program that adds two unsigned integers using AND, XOR and left shift #include <stdio.h> void main(void){ unsigned int x, y, carry; printf("x="); scanf("%u", &x); printf("y="); scanf("%u", &y); while(x!=0){ carry=x&y; y=x^y; carry=carry<<1; x=carry; } printf("sum=%u", y); }
References Clint HICKS: Utilizare C. Uşor şi repede, Teora Printing House, 1996, ISBN 973-601-335-9 Cprogramming http://www.cprogramming.com Wikipedia http://en.wikipedia.org