COMP2401 Chapter 2 - Data Representation PDF

Summary

This document is a chapter from a course on computer science, specifically on data representation. It covers various bit models and number systems, including binary, decimal, octal, and hexadecimal, along with examples and basic C code illustrations.

Full Transcript

Chapter 2 Data Representation What is in This Chapter ? This chapter explains how data is represented in memory. It begins with an explanation of how bits are stored using various bit models (i.e., magnitude only, sign magnitude, two’s compliment, fixed point, floating poi...

Chapter 2 Data Representation What is in This Chapter ? This chapter explains how data is represented in memory. It begins with an explanation of how bits are stored using various bit models (i.e., magnitude only, sign magnitude, two’s compliment, fixed point, floating point, ASCII and UNICODE). It then discusses bit operators that help us understand how to manipulate memory data at the bit level which will allow us to make efficient use of storage space. Compound data types are introduced, such as Strings and Arrays (single and multi-dimensional). The chapter concludes with a discussion of custom type definitions such as structs and unions. COMP2401 - Chapter 2 – Data Representation Fall 2020 2.1 Number Representation and Bit Models All data stored in a computer must somehow be represented numerically in some way whether it is numerical to begin with, a series of characters or an image. Ultimately, everything digitally breaks down to ones and zeros. We need to understand how data is stored (or represented) in the computer so that we interpret the 1’s and 0’s correctly. For example, consider this sequence of ones and zeros: 01000011 01001111 01010111 The data can be interpreted in different ways: Three unique integers: 67, 79, 87 One large number: 4,411,223 A word: COW A color (RGB): The “correct” way to interpret the data depends on the context in which it is used. Numerical values that we use normally every day are in base 10 … the decimal system. In this system, a sequence of digits such as 62389 is easily understood to be “sixty-two thousand three hundred and eighty-nine”. We know this because we perform the following operation in our head: (9 * 1) + (8 * 10) + (3 * 100) + (2 * 1,000) + (6 * 10,000) Which is the same as doing this: (9 * 100) + (8 * 101) + (3 * 102) + (2 * 103) + (6 * 104) When dealing with computers, we often use other number systems as well such as Hexadecimal, Octal and Binary. Here is a comparison of these number systems: Number System Base Digits/Characters Used Example Binary 2 0, 1 1001011 Octal 8 0, 1, 2, 3, 4, 5, 6, 7 113 Decimal 10 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 75 Hexadecimal 16 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F 4B Given a number in any of the non-decimal system formats, we can determine the value in the same manner as with our decimal number system, using the base as the multiplication factor. - 44 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Consider how to compute the value of the number 1001101? in the various bases: Base Calculation Decimal Value 2 (1*20) +(0*21) +(1*22) +(1*23) +(0*24) +(0*25) +(1*26) 77 8 (1*80) +(0*81) +(1*82) +(1*83) +(0*84) +(0*85) +(1*86) 262,721 10 (1*100)+(0*101)+(1*102)+(1*103)+(0*104)+(0*105)+(1*106) 1,001,101 16 (1*160)+(0*161)+(1*162)+(1*163)+(0*164)+(0*165)+(1*166) 16,781,569 Hopefully you can remember how to convert from one base to another base. If not, here is how to convert from Binary to Decimal, Octal and Hex: Here is how to convert from Octal and Hex to Decimal: Here is how to convert from Decimal to Binary and Hex: - 45 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Here is a C example which shows how to specify literal values for decimal, octal, hex and binary numbers: Code from bases.c #include int main() { unsigned char dec = 27; // = (2*10) + 7 = 27 unsigned char oct = 027; // = (2*8) + 7 = 23 unsigned char hex = 0xbf; // = (11*16) + 15 = 191 unsigned int hex2 = 0xbad; // = (11*256) + (10*16) + 13 = 2989 unsigned char bin = 0b00111100; // = (32 + 16 + 8 + 4) = 60 printf("%d %d %d %d %d\n", dec, oct, hex, hex2, bin); return 0; } Bit Models Consider now binary numbers. Each 1 or 0 is called a bit. We can group bits together. A group of four consecutive bits is called a nibble and a group of eight consecutive bits is called a byte. Two nibbles can be grouped to form a byte. We often break things down into nibbles when we do hexadecimal calculations since each hexadecimal digit is represented with a nibble. We can then group two, four or 8 bytes together for form a word. We can thus have a 16-bit word, a 32-bit word or a 64-bit word. When we group bits of 1’s and 0’s together, there are a variety of methods for interpreting them. Each method of interpreting the sequence of bits is called a bit model. We will look at 6 models: Magnitude-only Bit Model Sign-Magnitude Bit Model Two’s Compliment Bit Model Fixed-Point Bit Model Floating-Point Bit Model ASCII and Unicode Bit Model - 46 - COMP2401 - Chapter 2 – Data Representation Fall 2020 In C, the model that is used will depend on the data type. So, unsigned int, int, float and char will all have their own bit model. (Note: From here-on in the notes, it is assumed that sequences of 1’s and 0’s are binary numbers, and so the numbers will not have a base 2 subscript indication.) Magnitude-only Bit Model This model is for non-negative decimal numbers (whole numbers). It is the simplest model since each bit represents an integer power of 2. With an 8-bit value, we can store a value in the range of 0 to 255. 00000000 = 0 00000001 = 1 00000010 = 2 00000011 = 3 … 11111101 = 253 11111110 = 254 11111111 = 255 In this representation, we consider the leftmost bit to be considered the most-significant bit and the rightmost bit as the least-significant bit. (e.g., 10110101) Interestingly, when adding numbers, we simply add the bits together starting from the least- significant bit to the most-significant bit: Example of (10 + 7) Example of (254 + 7) 00001010 11111110 + 00000111 + 00000111 -------- -------- = 00010001 =100000101 OVERFLOW! = 17 = 5 Notice that there can be an overflow. Ultimately, there is a limit to the values that we can store with just one byte. Hence, we often use more than one byte to represent numbers in our software. When dealing with combinations of bytes, we can have groups of 8 bits to store words to provide a larger range of values. For example, a sequence of 4 bytes can represent a much larger number: 10011001 00001110 01111001 00111001 = (10011001 * 224) + (00001110 * 216) + (01111001 * 28) + (00111001 * 20) = (153 * 16,777,216) + (14 * 65,536) + (121 * 256) + (57 * 1) = 2,567,862,585 - 47 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Here is a table that shows the range of numbers that can be stored when using combinations of bytes together using the magnitude-only bit model: Word Size Bits Used Bytes Used Number Range byte 8 1 0 to 255 16-bit word 16 2 0 to 65,535 32-bit word 32 4 0 to 4,294,967,295 64-bit word 64 8 0 to 18,446,744,073,709,551,615 In C, the magnitude-only bit model is used with unsigned types: C – data type Bits Used Bytes Used Number Range unsigned char 8 1 0 to 255 unsigned short int 16 2 0 to 65,535 unsigned int 32 4 0 to 4,294,967,295 unsigned long int 64 8 0 to 18,446,744,073,709,551,615 Sign-Magnitude Bit Model This model allows negative decimal numbers (whole numbers). It is the simplest strategy for representing negative numbers. It has a smaller magnitude range though … allowing numbers only in the range of -127 to +127 for a single byte. The idea is to simply use the most- significant bit to be the sign bit (which is the reason for the reduction in magnitude using this bit model). By convention, a value of 0 in the sign bit indicates a positive value, while a 1 indicates a negative value. 00000000 = 0 00000001 = 1 00000010 = 2 … 01111110 = 126 01111111 = 127 10000000 = -0 10000001 = -1 10000010 = -2 … 11111110 = -126 11111111 = -127 This representation is not used often because of a couple of reasons. First, there are two values for zero … +0 and -0. That is weird. Even more of a hassle is that the math does not work out evenly. If the signs of the two numbers are the same, we simply add the magnitudes as unsigned numbers and leave the sign bit intact. However, we need to be careful about overflow: - 48 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Example of (10 + 7) Example of (-10 + -7) Example of (126 + 7) 00001010 10001010 01111110 + 00000111 + 10000111 + 00000111 -------- -------- -------- = 00010001 = 10010001 = 00000101 OVERFLOW! = 17 = -17 = 5 If the signs differ, we need to subtract the smaller magnitude from the larger magnitude, and keep the sign of the larger magnitude: Example of (10 + -7) Example of (-10 + 7) Example of (126 + -7) 00001010 10001010 01111110 - 10000111 - 00000111 - 10000111 -------- -------- -------- = 00000011 = 10000011 = 01110111 = 3 = -3 = 119 But this is unpleasant and a bit ugly. Because of the two-zero problem and the need to subtract instead of add … the sign-magnitude bit model is not often used. It is not used in C. Two’s Complement Bit Model This model allows both positive and negative decimal numbers (whole numbers). Regarding positive and zero numbers … things are represented the same way as a magnitude- only bit model. It has a smaller magnitude range though … allowing numbers only in the range of -128 to +127. To represent negative numbers … we take the bits that represent the number as if it were positive, then invert (or flip) all of the bits and then add 1. 00010011 = 19 11101100 flipped bits 11101101 added one ---------------------------- 11101101 = -19 As it turns out, if the most significant bit is 1, then the number is negative, just like the sign- magnitude bit model. To determine the magnitude of a negative number, we perform the exact same steps: 11101101 = -19 00010010 flip bits 00010011 add one ---------------------------- 00010011 = 19 magnitude We add numbers in the same way: - 49 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Example of (10 + 7) Example of (-10 + -7) 00001010 11110110 + 00000111 + 11111001 -------- -------- = 00010001 =111101111 extra 1 carried out, no problem = 17 = -17 It is possible, however, that there can be an overflow … resulting in the answer being invalid. It is easy to tell if an overflow has occurred. There are two cases: If the sum of two positive numbers results in a negative result. If the sum of two negative numbers results in a positive result. Example of (126 + 7) Example of (-80 + -100) 01111110 10110000 + 00000111 + 10011100 -------- -------- = 10000101 … sign bit changed! =101001100 overflow … sign bit changed! = -123 in two’s-complement = 76 in two’s-complement In C, the two’s complement bit model is used with signed types: C – data type Bits Used Bytes Used Number Range char 8 1 -128 to +127 signed char short int 16 2 -32,768 to +32,767 signed short int int 32 4 -2,147,483,648 to +2,147,483,647 signed int long 64 8 -9,223,372,036,854,775,808 to signed long +9,223,372,036,854,775,807 signed long int Here is a program that stores some positive and negative values in signed and unsigned chars (i.e., bytes). Since the range of signed chars is smaller than unsigned, you will notice some interesting results for values above 127. You will also notice some interesting results for values that go beyond the storage range of a char or byte. The compiler actually provides a warning, but allows the code to compile. Near the end of the code, some of these overflow values are stored in short data types … and you can see that there are no problems. It is important to understand that things may not always “appear” as you want them to when it comes to mixing signed and unsigned values. Be careful when printing out such values … especially when debugging: - 50 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Code from twosCompliment.c Output #include int main() { char sc; unsigned char uc; sc = uc = 0; //00000000 printf("0 signed: %d\n", sc); 0 signed: 0 printf("0 unsigned: %d\n", uc); 0 unsigned: 0 sc = uc = 1; //00000001 printf("1 signed: %d\n", sc); 1 signed: 1 printf("1 unsigned: %d\n", uc); 1 unsigned: 1 sc = uc = 7; //00000111 printf("7 signed: %d\n", sc); 7 signed: 7 printf("7 unsigned: %d\n", uc); 7 unsigned: 7 sc = uc = 126; //01111110 printf("126 signed: %d\n", sc); 126 signed: 126 printf("126 unsigned: %d\n", uc); 126 unsigned: 126 sc = uc = 127; //01111111 printf("127 signed: %d\n", sc); 127 signed: 127 printf("127 unsigned: %d\n", uc); 127 unsigned: 127 sc = uc = 128; //10000000 printf("128 signed: %d\n", sc); 128 signed: -128 printf("128 unsigned: %d\n", uc); 128 unsigned: 128 sc = uc = 255; //11111111 255 signed: -1 printf("255 signed: %d\n", sc); 255 unsigned: 255 printf("255 unsigned: %d\n", uc); sc = uc = 256; //100000000 *overflow 256 signed: 0 printf("256 signed: %d\n", sc); 256 unsigned: 0 printf("256 unsigned: %d\n", uc); sc = uc = 260; //100000100 *overflow 260 signed: 4 printf("260 signed: %d\n", sc); 260 unsigned: 4 printf("260 unsigned: %d\n", uc); sc = uc = -0; //100000000 *overflow -0 signed: 0 printf("-0 signed: %d\n", sc); -0 unsigned: 0 printf("-0 unsigned: %d\n", uc); sc = uc = -1; //11111111 -1 signed: -1 printf("-1 signed: %d\n", sc); -1 unsigned: 255 printf("-1 unsigned: %d\n", uc); sc = uc = -7; //11111001 -7 signed: -7 printf("-7 signed: %d\n", sc); -7 unsigned: 249 printf("-7 unsigned: %d\n", uc); sc = uc = -126; //10000010 -126 signed: -126 printf("-126 signed: %d\n", sc); -126 unsigned: 130 printf("-126 unsigned: %d\n", uc); - 51 - COMP2401 - Chapter 2 – Data Representation Fall 2020 sc = uc = -127; //10000001 -127 signed: -127 printf("-127 signed: %d\n", sc); -127 unsigned: 129 printf("-127 unsigned: %d\n", uc); sc = uc = -128; //10000000 -128 signed: -128 printf("-128 signed: %d\n", sc); -128 unsigned: 128 printf("-128 unsigned: %d\n", uc); sc = uc = -255; //00000001 *weird eh? -255 signed: 1 printf("-255 signed: %d\n", sc); -255 unsigned: 1 printf("-255 unsigned: %d\n", uc); sc = uc = -256; //100000000 *overflow -256 signed: 0 printf("-256 signed: %d\n", sc); -256 unsigned: 0 printf("-256 unsigned: %d\n", uc); sc = uc = -260; //011111100 *overflow -260 signed: -4 printf("-260 signed: %d\n", sc); -260 unsigned: 252 printf("-260 unsigned: %d\n", uc); short ss; unsigned short us; ss = us = 255; //0000000011111111 255 signed short: 255 printf("255 signed short: %d\n", ss); 255 unsigned short: 255 printf("255 unsigned short: %d\n", us); ss = us = 256; //00000000100000000 256 signed short: 256 printf("256 signed short: %d\n", ss); 256 unsigned short: 256 printf("256 unsigned short: %d\n", us); ss = us = 260; //00000000100000100 260 signed short: 260 printf("260 signed short: %d\n", ss); 260 unsigned short: 260 printf("260 unsigned short: %d\n", us); ss = us = -255; //1111111100000001 -255 signed short: -255 printf("-255 signed short: %d\n", ss); -255 unsigned short: 65281 printf("-255 unsigned short: %d\n", us); ss = us = -256; //1111111100000000 -256 signed short: -256 printf("-256 signed short: %d\n", ss); -256 unsigned short: 65280 printf("-256 unsigned short: %d\n", us); ss = us = -260; //1111111011111100 -260 signed short: -260 printf("-260 signed short: %d\n", ss); -260 unsigned short: 65276 printf("-260 unsigned short: %d\n", us); return 0; } - 52 - COMP2401 - Chapter 2 – Data Representation Fall 2020 The three bit-models that we just looked at are used for representing whole numbers. But how do we represent real (i.e., non-whole) numbers ? There are two main approaches: 1. Fixed point Pros: o faster than floating-point arithmetic ▪ Used in digital signal processing and game applications where performance is sometimes more important than precision Cons: o loss of range for integer portion if more precise fraction is needed o works for fractional powers of 2 but not for powers of 10 2. Floating point Pros: o much better precision and range Cons: o slower than fixed-point arithmetic Fixed-Point Bit Model The fixed-point bit model is used to represent real numbers, such as floats and doubles. The key to the fixed-point bit model is based on the concept of a binary point … which is like the decimal point in a decimal system that separates the integer part from the fractional part. Consider the binary point number as follows: 11010.101 This can be calculated as follows: (1*24) + (1*23) + (0*22) + (1*21) + (0*20) + (1*2-1) + (0*2-2) + (1*2-3) = (1*16) + (1*8) + (0*4) + (1*2) + (0*1) + (1*1/2) + (0*1/4) + (1*1/8) = 16 + 8 + 2 + 1/2 + 1/8 = 26.625 Interestingly, if we shift the binary point to the left, we end up with a number half the size: 1101.0101 = 13.3125 and if we shift the binary point to the right, we end up with a number twice the size: 110101.01 = 53.25 So … the position of the binary point is crucial in determining the result. In a fixed-point representation, therefore, we must always know how many bits are being used (a.k.a. the width) … and where to position the binary point (a.k.a. the binary point position). - 53 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Consider this number: 11010101 As we have seen it can represent various values, depending on where we place the binary point. We can use the notation fixedPoint(w,b) to represent a w-bit number with the binary point position set to have b digits to the right of the point: Notation Fixed-Point Binary Real Number fixedPoint(8,0) 11010101 213 fixedPoint(8,1) 1101010.1 105.5 fixedPoint(8,2) 110101.01 53.25 fixedPoint(8,3) 11010.101 26.625 fixedPoint(8,4) 1101.0101 13.3125 fixedPoint(8,5) 110.10101 6.65625 fixedPoint(8,6) 11.010101 3.328125 fixedPoint(8,7) 1.1010101 1.6640625 fixedPoint(8,8).11010101 0.83203125 Negative numbers are represented as either sign-magnitude or two’s compliment. Assuming two’s compliment, we represent the number -7.25 as follows: 000111.01 = 7.25 … positive number 111000.10 = flip bits … 111000.11 = add 1 to get negative number -7.25 Number addition is done the same way as two’s compliment. We basically just “ignore” the decimal when adding: Example of (2.5 + 1.75) Example of (-2.5 + -7.25) 000010.10 111101.10 + 000001.11 + 111000.11 --------- --------- = 000100.01 =1110110.01 = 4.25 = -9.75 Floating-Point Bit Model With the floating-point representation, we can use the available bits in different ways. As a result, the total precision using 4 bytes is around 8 digits. That means, we can represent numbers like this: 0.123456, 123.456 or 123,456.0 … but we cannot represent numbers like this: 123,456.789012. With the floating-point model, the binary point position is not fixed … but may move around (i.e., float). In the decimal number system, a number is written in scientific notation like this: 28410 = 2.84 x 102 - 54 - COMP2401 - Chapter 2 – Data Representation Fall 2020 In general, a number is written in scientific notation as: ± M x BE where M = mantissa, B = base and E = exponent In C, we have two types that represent real numbers: C – data type Bits Used Bits used - Exponent Bits used - Mantissa float 32 8 23 double 64 11 52 There are many ways to represent a floating-point number. Here is one way to represent the number 284: 284 = 1000111002 = 1.000111 x 28 1-bit sign 8-bit exponent 23-bit mantissa 0 00001000 100 0111 0000 0000 0000 0000 Since the leading digit in the mantissa is always 1 (for non-zero values), we can assume that this is implied in an improved representation as follows: 1-bit sign 8-bit exponent 23-bit mantissa 0 00001000 000 1110 0000 0000 0000 0000 In the IEEE 754 32-bit floating-point standard, we add a bias of 127 to the exponent as follows: 1-bit sign 8-bit biased exponent 23-bit mantissa 0 10000111 000 1110 0000 0000 0000 0000 There are some special cases: Number 1-bit sign 8-bit biased exponent 23-bit mantissa 0 00000000 000 0000 0000 0000 0000 0000 ∞ 0 11111111 000 0000 0000 0000 0000 0000 -∞ 1 11111111 000 0000 0000 0000 0000 0000 NaN 11111111 Non-zero NaN is short for “Not a Number” and it is used to represent a number that does not exist. When using double-precision numbers, the bias used should be 1023 instead of 127. Otherwise things work the same way. Adding floating point numbers is a little more work that non-floating-point numbers. - 55 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Consider adding the following two real numbers: 345.7500 + 6,819.1875 ------------ 7,164.9375 How do we add these numbers using floating point notation? First, get them into binary and then scientific notation: 0000101011001.1100 = 1.010110011100 x 28 1101010100011.0011 = 1.1010101000110011 x 212 And now into floating-point representation: 0 10000111 010 1100 1110 0000 0000 0000 0 10001011 101 0101 0001 1001 1000 0000 Now to add them, there are a few steps to take: 1. Get the two mantissas, putting the “1.” before both fractional parts: N1 = 1.0101100111 N2 = 1.1010101000110011 2. Compare the exponents by subtracting the 2nd exponent from the 1st: 135 – 139 = - 4 Since this is a negative number, we shift the binary point of N1 right by 4 bits so that the numbers both have exponent 212 now: N1 = 0.0001010110011100 N2 = 1.1010101000110011 3. Add the mantissas: 0.0001010110011100 + 1.1010101000110011 -------------------- 1.1011111111001111 4. Normalize the mantissa if it has more than one digit to the left of the binary point 5. Round the result if need be (but should not need to if still fits in 23 bits) 6. Assemble the exponent and mantissa back into floating-point notation 0 10001011 101 1111 1110 0111 1000 0000 - 56 - COMP2401 - Chapter 2 – Data Representation Fall 2020 7. Verify if answer is correct: = 1.10111111110011110000000 x 212 = 1101111111100.11110000000 = 7164 + 0.9375 = 7164.9375 As you can see … it can be a little tricky! It is always important to verify that your calculations are correct. ASCII and Unicode Bit Model This model is for representing non-numerical values. It is a way of mapping characters to numbers. ASCII (American Standard Code for Information Interchange) and Unicode (not a real acronym but stands for a Universal code standard) are two ways of representing characters. The ASCII code standard was released in 1963, and was subsequently modified in 1967 and again in 1986. It is a subset of the Unicode standard which was released in 1991. Unicode incorporates characters from different languages. Original ASCII code mapped non-accented English text characters and punctuations to numbers. It also mapped some control characters (e.g., NULL, whitespace, tabs, newline, separators) to numbers as well. Each character is contained in one byte. Here are the standard mappings from 0 to 255: - 57 - COMP2401 - Chapter 2 – Data Representation Fall 2020 In C, there are 2 character-types that make use of ASCII: C – data type Bits Used Bytes Used Conversion unsigned char 8 1 Decimal value converted to binary using magnitude-only char 8 1 Decimal value converted to binary signed char using two’s compliment When dealing with UNICODE characters, there are various encoding schemes. UTF-8 (i.e., Unicode Transformation Format) is most commonly used to represent characters from other languages. Each character can take up to 4 bytes. But in C, we use a short int to hold a 2- byte Unicode value. There is a LOT to say about the Unicode text format, but we do not want to get into it too much in this course. We will focus mainly on ASCII code. - 58 - COMP2401 - Chapter 2 – Data Representation Fall 2020 2.2 Bitwise Operations Consider an internet company that has 1.2 million people using its services. Perhaps the company wants to keep track of some simple boolean values per customer … such as whether they are an adult, whether they are currently logged-on, or something as simple as whether their account is active. This can be done using an array: char loggedOn; The char data type is the smallest primitive type in C, which uses 1 byte … or 8 bits. We would set each value of the array to be either TRUE (i.e., 1) or FALSE (i.e., 0), depending on the log-on-status of that customer. The only values that will be stored in the array are 1 and 0. It is easy to see that this is a poor use of space. We would actually be making use of just 1 of the 8 bits in each byte. So we are wasting 87.5% of the storage space required by the array!!! A simple solution is to allow 8 people’s log-on-status booleans to be grouped together and stored in a single byte. In addition to this memory storage problem, sometimes we might need to access data coming in from (or out to) a hardware port in which a byte (or set of bytes) contains portions of bit sequences that have particular meaning, such as on/off flags, error codes, data, instruction code, etc.. At any given moment, we many need to read or set the bits that are relevant for what we are trying to do. It is for good reason then that we will need a way of manipulating bytes at the bit- level. That is what this section of the notes is all about … understanding how to manipulate the bits. A bit operator is an operator that takes one or two numbers and performs an operation on the bits of those numbers. They are symbols that are used to manipulate individual bits of whole number data types such as signed char, unsigned char, signed int and unsigned int. These operators can be used on literals or variables. Here is a list of available bit-operators in C: - 59 - COMP2401 - Chapter 2 – Data Representation Fall 2020 Operator Action Description ~ bitwise NOT inverts every bit (works on one value) & bitwise AND performs AND between every bit (requires two values) | bitwise OR performs OR between every bit (requires two values) ^ bitwise XOR performs Exclusive OR between every bit (requires two values) >> right- shift moves bits into lower-order (less significant) bit positions >1; printf("\n157 >> 1 = %d, 10011101 >> 1 = ", answer);printAsBinary(answer); answer = n1>>2; printf("157 >> 2 = %d, 10011101 >> 2 = ", answer);printAsBinary(answer); answer = n14; printf("-100 >> 4 = %d, 10011100 >> 4 = ", answer2);printAsBinary(answer2); return 0; } - 60 - COMP2401 - Chapter 2 – Data Representation Fall 2020 // Convert an unsigned char to an integer that looks like binary void printAsBinary(unsigned char n) { for (int i=7; i>=0; i--) { if ((int)(n/pow(2,i)) > 0) { printf("1"); n = n - pow(2,i); } else printf("0"); } printf("\n"); } The output of the program is as follows: ~157 = 98, ~10011101 = 01100010 157 >> 1 = 78, 10011101 >> 1 = 01001110 157 >> 2 = 39, 10011101 >> 2 = 00100111 157 4 = 11111001 Notice that during a right bit shift, zeros are added in on the left as the most significant bit. Similarly, when shifting left, zeros are added in on the right as the least significant bit. This is always the case when dealing with unsigned integers (i.e., magnitude only). However, when right-shifting with negative numbers (i.e., two’s complement), the bits coming in are 1’s … the highest order sign bit. Examining bitmasks In order to extract portions of bits from numbers, we need to use … A bitmask is a sequence of one or more bits that you apply to another binary number to read, set or clear the value of one or more bits. The bitmask number is used to indicate which bits are to be affected by an operation. The following table shows how to read, set and clear a particular bit (i.e., the nth bit) of a number: Operation Solution in C code Set nth bit x OR 2n x = x | (1

Use Quizgecko on...
Browser
Browser