|Fundamental C - Arithmetic and Representation|
|Written by Harry Fairhead|
|Monday, 27 May 2019|
Page 1 of 2
This extract, from my new book on programming C in an IoT context, explains the basics of numeric representation including what one's-complement and two's-complement are all about.
Fundamental C: Getting Closer To The Machine
Now available as a paperback and ebook from Amazon.
Also see the companion volume: Applying C
Arithmetic and Representation
A key idea in using C is that what is stored in a variable is a bit pattern and what it means is up to how you process it. In this chapter we look at the most fundamental representation - simple binary integers. This is enough for many applications, but sooner or later you are going to need negative integers as well. What is surprising is that the change from unsigned int to int is just a matter of changing how you interpret the bit patterns.
A set of bits most naturally represents a positive integer. You probably are already familiar with binary but it is worth remembering that it is just counting in base 2. The first bit, usually denoted as bit 0, or the least significant bit, represents 0 or 1. The next bit represents 0 or 2, the next 0 or 4 and so on. The last bit you use has the highest value and is generally called the most significant bit. As bit patterns are stored in variables of a fixed size, i.e. a fixed number of bits, there is always a most significant bit which can be zero or one. For example, for a char variable which can hold eight bits the most significant bit is bit 7.
To convert a binary number to decimal you simply add up all the powers of two. For example, 1110 in decimal is:
1*8 + 1*4 + 1*2 + 0*1 = 14
Converting from decimal to binary is just a matter of finding out how many lots of each power of two there are in a given number. For example, to convert 14 to binary you first notice that there is one lot of 8 which leaves 6, which in turn has one lot of 4 with 2 left over, giving:
Notice that you can read any bit pattern as if it was a positive integer. For example, if the bit pattern 1110 corresponds to the state of three lights – light 3 on, light 2 on, light 1 on and light 0 off – you can still read the bit pattern as 14.
We have already had occasion to use hexadecimal notation – assembly language uses it by default. Hexadecimal counts in lots of 16, just as decimal counts in lots of 10 and binary lots of 2. To count a single lot of 16 needs 16 symbols and these are usually taken to be 0,1..9, A, B, C, D, E, F. That is, A is 10, B is 11 and so on to F which is 15. Once you get to 15+1 you need another symbol to represent one lot of 16, i.e. 10 in hex is 16 in decimal. Notice that as the second hex symbol signifies lots of 16 you can write a large number with only two hexadecimal places. For example, FF is 255. As the next hexadecimal place is in terms of lots of 16x16, i.e. 256, with three hexadecimal symbols you can get as large as FFF, i.e. 4095.
Compactness is one of the advantages of hex. However, it is really useful because it is very easy to convert from hex to binary and vice versa. As a single hex symbol represents 0 to 15 and four bits also represents 0 to 15, you can convert a binary number to hex four bits at a time. For example:
0101 1110 = 5E
because 0101 is 5 and 1110 is E and both represent 94 in decimal.
Hex is really convenient for expressing the content of variables because two hex symbols specify a byte. So to specify the value of a char you only need two hex symbols, to specify an int you only need four or eight depending on whether it is a 16-bit or 32-bit number.
As already mentioned, C has hex literals signified by starting with 0x and this is an almost universal convention for denoting hex even when not writing C. It is important to get used to reading hex and being able to convert between hex and binary.
If you are wondering why hex is preferred to octal notation, it is simply that a single octal symbol corresponds to just three bits. You need three octal symbols to specify a byte. Even so you will occasionally find octal used to specify a bit pattern.
|Last Updated ( Monday, 27 May 2019 )|