 So, we will start with a brief recap of the first lecture. We discussed what a typical personal computer looks like. It has a motherboard with the CPU on it. The CPU is cooled by a fan. The CPU is connected by a lot of wires to a very fast electronic random access memory and there are also connectors for a bunch of cables that go to a hard disk. The memory and the hard disk are modeled as a giant two-dimensional array of bits. It's eight bits across that's called a byte and as many rows as you can afford. And the logical model of a CPU has an arithmetic and logic unit with a bunch of registers which are connected by the address and data bus to the main memory. At the moment we are not thinking about disks very consciously and we model the input and output devices as just specially mapped regions of RAM that you can read and write to interact with the outside world. The rules of the game are that you can fetch a memory location into a register. You can operate on two or more registers and save the value into a register and then you can push the value of a register into some other location of RAM. At the very lowest level that is the language in which you program computers. We are looking at a bunch of data between registers and RAM and operating on registers inside the arithmetic and logic unit but that is very tedious to code in. So we started looking at high level languages where there are high level statements with variable names which are abstract and symbolic that you can remember and understand easily and then statements which assign values to those variables which compute values in high level arithmetic expressions and then we started slowly getting into the concrete syntax of C++, how to include the IO library, how to print things to the screen, how to read things from the keyboard and so on. So that was the first lecture. How you and in the first week of labs we looked at how to edit a C++ source code file, save it to disk, compile it using the G++ compiler and to run it from what is called the command line of bash which is a shell which runs on top of the operating system itself. So that was the first lecture. Today we will continue with a discussion of the basic data types which are implemented on top of the very low level bit array memory that the hardware provides. So we will discuss what are various types, basic types supported by the language, how to declare variables that belong to those types, why declare at all, how to write down arithmetic logical expressions and then how to assign those expressions to the variables. Before we get into the business, a few announcements. If you fail to use the attendance machines on the sides of this hall then please write down your name on a piece of paper and give it to either me or any TA who may be around in the lecture. We will process those attendances manually later on. All lab batches should be stable now. They are linked from the static web page for the site. So click through to Google Docs, search for your roll number and name, verify that the name and roll number are correct and then check which evening you should come to lab. Your lab batch also defines in part the junior TA who is assigned to you. So your first line of defense in case you are in trouble, don't understand something would like to go to a makeup lab whatever is to talk to your personally assigned junior TA and if you cannot settle or resolve things with the JTA then you escalate to the senior TA and then to me. Labs this week will continue with familiarization and small programs. In particular we will look at handling command line arguments. So when you say move file 1 to file 2, file 1 and file 2 are called command line arguments to the program called move. So when you write your own programs, you accept those inputs from the outside world through these two variables that are passed into main. So that's one of the things you'll learn. Then we'll write some logical expressions like Boolean expressions that we'll discuss today and we'll see that sometimes surprising things happen because of finite precision in the computers. There will be no tutorials this week either but we will begin posting homework that you can work out on paper on Moodle. You should see them by the end of tomorrow and this will give you an impression of exam questions but it will also help you understand the lectures better. These will not be graded but you should do them regularly and these problems will be discussed in the tutorials of the following week. So when you start tutorials next week onwards we'll discuss problems set out in this week. So as I was saying the computer memory is measured in units of storage that are bits which are 0 and 1 and the reason for not choosing a number system with a larger number of distinct values like the decimal system with 10 values is that electronically it's easier to build circuits that distinguish between two voltage levels and if you try to represent 10 voltage levels or even more then noise may affect the circuits worse than if you just use two levels. Now these bits are collected into units called bytes which are 8 bits wide. So if you write down 8 bits each of which is 0 or 1 side by side then they can go from all these values which is all 0s to start with. Now in decimal what happens you start with 0 then you increment it by 1 and then you increment it by 2 until you hit 9 after which you cannot increment any more and you carry over to 10. So in binary the next number is by incrementing this bit which then becomes a 1 the rest are still 0s but right in the next step there is a carryover because there are no more digit level. So the next number is 1 0 that corresponds to 2. So this is decimal this is binary and if you keep doing this flip each bit there are two possible values there are 8 bits. So the last value you see here is all 1s and that corresponds to the number 255 and that is because the first position the rightmost position or the least significant bit is worth 1 the next bit is worth 2 the next bit is worth 4 until this bit is worth 128. So just like in decimal the worth of this position is 1 the worth of that position is 10 and so on here the worth increases in powers of 2 rather than powers of 10. So this would be the case if we wanted to count strictly positive or non-negative numbers. So 2 to the power 8 or 256 possible bit configurations and if we choose to we can interpret them as integers between 0 and 255 which in C and C++ is called the type unsigned character unsigned meaning it does not have a sign it cannot be plus or minus it is only plus. So in ordinary computers all electronic and magnetic memory is allocated in units of bytes. English based or western character sets are such that 256 values are typically enough to encode all the standard characters in the typed language and that is one of the reasons why unsigned character is not called like unsigned byte although in Java there is a distinction between a byte and a character because in Java characters can be multiple bytes because it has to support many languages from around the world. Sometimes people write down a byte as consisting of what are called 2 nibbles. So if you write byte as biting of something nibbling is like nibbling half of it. So that is called a nibble and to ease our writing through this lecture we will talk about 4 bits because we can look at the 16 possible values more easily. Now so nibble ranges between 0 and 15, 15 is the last value and a nibble is usually written in hexadecimal. Xadecimal is just a number system which starts at 0 and ends at 15 except that we do not want to write it in decimal because it takes two characters. So after 9 instead of writing 10 we write A, B, C, D, E, F that is one hexadecimal digit. So in other words a byte value written in hexadecimal can range from 0, 0 through FF. So with a little practice you can get conversion doing arithmetic in hexadecimal and a typical hexadecimal number may look like 0A or C5. So initially we might need to write down the correspondence explicitly because it is not easy to remember this. So up to 9 is standard, A is 10, B is 11, C is 12, B is 13, E is 14, F is 15. So in this system if I write 0A that number corresponds to just 10 that is 0A, 0Ax is equal to 10 decimal. And what is C5? C5 is C times 16 plus 5 and C being 12 this is 12 times 16 plus 5. So that so C5 in hex is equal to 12 times 16 plus 5 decimal. So if I write down the number 23 in decimal what do I mean? I mean 2 into 10 plus 3. Now the base has become 16 in hexadecimal digits. So that is how you should read hexadecimal numbers. Now unfortunately there may be some ambiguity about whether 2 3 is 23 in decimal or 23 in hexadecimal. So if you are writing down literal strings in your code in C or C++ or Java to make clear whether you mean hexadecimal or decimal in case of hexadecimal you have to write it as 0x23. That tells the compiler that you mean 23 in hexadecimal and not decimal. If you write only 2 3 that is decimal 23. So let me give a quick demo to show you how this works. It also shows you a C library called stdio. Although C++ also provides comparable libraries some programmers often find it more convenient to use a couple of C libraries for reading and writing values from the screen and to the screen and from the C board. So here is one example. So I will comment out a bunch of things and let us say int a equals 0x. What do I have here? C5 right C5 and now I am going to just print a. We will see what that comes out to. I am going to comment that part. So if I now compile. So it prints 197. Does that look correct? So 92 plus 5 that is 197 or suppose you wanted to print in hexadecimal format which you often want to do for system programming. The right way to do it is to write print f, print formatted and then you give two things. One is called the formatting string and the other is the value itself. In the formatting string I will say that the next thing to print is a itself and you say that by saying a percent and then you say percent x for hexadecimal and you say print a new line at the end of it. So this print f statement tells the compiler to produce an output which is the hexadecimal representation for a followed by new line. If I compile that and run it you get C5 back again. Now I was talking about bytes here but we have already introduced an int variable. So what is an int variable? In ram looking at ram as an endless sequence of bytes. Here is one particular byte with 8 bits. Here is the next byte again with 8 bits. You could either think of this byte as a byte or unsigned character variable having 8 bits or you could think of 4 consecutive bytes as a 32 bit integer and that gives you more bits and more combinations. We will soon see how many combinations we can get and therefore a 32 bit integer has 4 bytes or it has 8 nibbles. In other words it has 8 hexadecimal digits. So all standard integers in C and C++ which are 32 bits wide have a 8 hexadecimal digit representation. So suppose I want to print out to a field width of 8 and print all the leading 0s if necessary that will help us understand numbers a bit better. So I can say that by saying the field width to print is 8 and in case there are leading 0s print them also. So I say percentage 0 8 x on the new line if I compile that and run it I get all the leading 0s and then C5. So as I was saying you get only 256 combinations with a byte and that is not usually enough for doing most programming. So the language provides integers of a few fixed sizes. Apart from bytes you can get what are called short integers or short for short that are 16 bits wide. So you take 2 bytes consecutively and that becomes your 16 bit number and if you go through the same exercise again you will obviously see that the number of combinations now instead of being 2 to the power 8 is 2 to the power 16 and that gives you 65536 combinations. So about 65 and half thousand possible numbers. There is one interesting convention that you need to be aware of if I am taking up 2 bytes worth of space for a short number this is the short data type. There are 2 ways of doing that if I wrote down the number in standard format where least significant bit is here and most significant bit is there out of 2 bytes. Now in this packing of the 2 bytes into a short I can either have the LSB here or I can have the LSB there. In earlier days there used to be computer architectures which followed either of these conventions. One was called the little endian and the other was called the big endian not Indian but endian whether the little byte goes in the end or the big byte goes in the end. Now you need to be careful about this only if you are transmitting data between 2 computers having incompatible architectures and you are doing that in a low level data format then you need to worry about this but modern languages like Java have taken this problem away because they have a standard encoding mechanism across architecture. Now even 65536 possible values may not be enough to cover all your programming needs you may well have you know arrays with millions of entries in it so 65,000 is nothing. So the next size of integer that's provided is 4 bytes that's the standard integer which is 32 bits that gives you about 4.3 billion distinct values and that's used adequate for most programming purposes including indexing arrays and all kinds of things except that there are these large amount things like if you're writing code for say I don't know astronomy or finance particularly then it's not enough to have 4 billion as the maximum possible number in which case you might want to use what are called long integers or longs which are 64 bits wide so it's 8 bytes one after the other all packed into or unraveled into a 64 bit number 8 times 8 bits. Now these are all what are called fixed size integer types and they count up in discrete steps all their 2 to the power bit values are can be equally used but they are not enough for scientific applications where you have to represent really astronomical distances literally distance between Sun and Pluto and other stars and so on for which we need scientific notation so it's like 6.5 into 10 to the power 23 so how do you represent those those are done using what are called floating point and double precision floating point numbers whereas byte short int long character those are all called fixed point numbers. Now before we get into particular number let's finish off most of what we have to do with fixed point numbers so we saw that in case of 4 bit number we can choose to represent only non-negative numbers between 0 and 15 but obviously that's not adequate even if you're writing a banking application you might want to represent debits and credits with signs and you might want to cancel off debts by payments and so on so you need both positive and negative numbers. Now a very simple way to represent positive and negative numbers is to set aside one of the bits as a sign bit. So suppose I was given 4 bit position I say this is going to be my sign bit which can be either 0 or 1 0 might mean positive 1 might mean negative say and then these 3 bits can go through 8 combinations 0 0 0 to 1 1 1 so this is decimal 0 this is decimal 7 so now instead of getting only positive numbers between 0 and 15 I get both positive and negative numbers between 0 and plus minus 7 but note that 0 has a precarious position because 0 can be written 2 ways either as plus 0 or as minus 0 and so now I have 0 through plus 7 minus 0 through minus 7 I thought those were 16 distinct values but actually I've lost 1 so I get 15 distinct values and besides anywhere you're doing an arithmetic comparison between two variables one of them might have been said to plus 0 the other one might have been said to minus 0 they're actually the same thing so if you have this comparator circuits which ask if they are equal they have to be designed specially for one number that's ugly besides we are losing one possible value so what is the remedy the remedy is to use what's called two's complement arithmetic which goes as follows we want to represent both positive and negative numbers with this bit sequences say four bits and so what we do is we do the same thing as before for the positive guys so we say that as long as the first bit is 0 that constitutes my set of positive numbers so although it's a little tedious I'll write down the whole set because it'll be easier to look at them in future so I start at 0 0 decimal that's one decimal so that's the positive numbers and then I have eight more values left right I've exhausted eight values in the positive non-negative numbers so I've eight more values left so what I do is we assign them between minus 8 to 1 and we do it in a particular way we basically say if I clock this up one more I would get 1 0 0 0 right I'll actually stick that right at the top 1 0 0 0 and call it minus 8 then I increase it again then I'll get that and I'll increase this also by 1 so that's minus 7 so 1 0 1 0 will be minus 6 1 0 1 1 will be minus 5 and all ones will be minus 1 now this looks like an arbitrary design choice but it's actually very well considered and it has a lot of nice properties now the first thing that might jar you a little bit is that in the positive number world it's felt very natural to go from 0 1 1 1 to the next number and I should have really kept on going positive this looks like a sudden jump from the highest possible positive value represented to the lowest possible or negative value represented right whereas earlier my sense of a wrap around was here that's when the clock rolled over and I started again from the beginning but now this has become much more natural transition from minus 1 to 0 whereas something that looked like a more natural transition from 0 1 1 1 to 1 0 0 has now become a jump from the highest positive to the largest magnitude negative value so this is called two's complement representation you can see the chart also here for only the negative values so the wrap around happens from the maximum value which in binary looks like a 0 followed by all ones to the most negative value which is one followed by all zeros and then minus 1 in in this two's complement system is always all ones and zero is always all zero now this has many many important properties so I'll just tell you one which is suppose I take the number say 5 which is 0 1 0 1 let me complement each bit if I do that I get 1 0 1 0 let me add 1 to it I get 1 0 1 1 which is minus 5 so that's why it's called two's complement you can take a number you can negate all the bits that doesn't directly give you the negative of the number to get the negative you add binary one that's how you negate a number now let's verify that if you add a number and it's negation you get 0 you should do that right so minus 5 was 1 0 1 1 plus 5 was 0 1 0 1 if I add them up 1 plus 1 is 0 carry over 1 1 plus 1 is 0 carry over 1 carry over 1 and the rest overflows forget about infinite precision arithmetic if a carry over bit is found it just thrown away silently you never know about it so just like in decimal if I say 64 plus 26 what happens if you add 4 and 6 you get 10 that overflows the last number 9 so you do a carry over here so in binary there are only two possible values if they add up to 2 you can't write down 2 here so go to 0 and then you carry over 1 that's two's complement arithmetic it has certain other interesting properties that we'll explore in the tutorial so there's one sudden wrap around from 7 to minus 8 in case of 1 nibble 4 bits now this works exactly the same way for short int and long ints with their corresponding wrap around point maximum value and minimum value okay so observe that if I wrote down the number line 0 is so there is a equal number of configurations with a 1 in the left most position and 0 in the left most position right one of them is 0 and that's a little asymmetric because it's the first value to be used on the positive side or non-negative side and then strictly positive numbers you get one fewer than strictly negative numbers 0 is included on the positive side or so-called you know side with zeros on the first significant bit and when you tap around from large positive values to large negative values the system will not detect it will not get any warning there'll be no exceptions thrown so if your program necessarily uses values which are close to the positive or negative edge of the universe you need to be very careful in doing arithmetic and checking for the results to make sure that unintended things are not happening I'll give one example of that now if you're unhappy with fixed precision integers as provided by the hardware you can always fake it in software we'll see that in later labs there's no reason to say that my integers have to be all of a fixed size of 4 or 8 bytes if you have the space in your RAM you should be able to represent arbitrary precision integers and there are libraries for that you can write your own with a little effort but because that is supported in hardware operations on such integers will take much much longer than fixed precision integers which can be processed in a few cycles so typically today CPUs have a certain clock speed when you buy a CPU it will say 2.5 gigahertz or 2 gigahertz so one cycle is one over that amount of seconds and two integer additions can typically done in at most two to three cycles whereas if you implement this in software you'll take hundreds of cycles if not more to do a single addition so software arbitrary precision integers are slow but sometimes you need that because you want exact numbers to be preserved so let's look at our example code and a few other things so they were saying there's more code so now I'm going to well I can't read declare in that's a that the compiler will complain so I'll comment that one out now so now I am initializing a to the last or largest possible positive value in hexadecimal the number 7 is 0 1 1 1 so if you write down a large you know int or long in hexadecimal and it's the largest possible positive value it will always look like 7 followed by f's f is all once and I'll write that down as a decimal I'll print that out and also print out is a hex and then I'll increase it by 3 you will see that it will result in what looks like a negative number so we start off with this largest possible 32 bit integer whose hex representation is 7 followed by 7 f's so that's the exact point I made before moving on we are writing this in hexadecimal bits base 16 remember that in base 16 numbers go from 0 to 7 8 9 a b c d e f okay in hex there are 16 digits and so if I'm trying to write down the number which is 0 1 1 etc and here is you know 8 bits the first 4 is a 7 the second 4 is f and then f for all the others so I initialize to the largest positive integer I incremented by 3 and I get a very negative integer that's what you see here and in hex though the thing looks natural enough the 7 has increased to an 8 8 is 1 0 0 0 right and the right most x digit has become 2 because I added 3 I added 3 here so in the lab you will study this more carefully by writing code and you know feeding it values of your own but this is the thing the c compiler will not bother that by adding a positive number to a positive number you've gotten a negative number this is perfectly possible because of finite precision arithmetic and if you use a long you're just going to get more mileage but eventually it is going to wrap around so one of the first thing you need to learn is some amount of defensive coding so that if your application may drive integer numbers near the edges of the representation you need to be extra careful to check the values and do the right thing so they were saying there are scientific applications for which integers are not enough and you do need this floating point number representation for reals so in decimal we write real numbers in scientific notation as a mantisa and some exponent so we can write 0.314 into 10 to the power 11 and there are some conventions about how to range the mantisa for example there's no point writing 314 597 into 10 to the power 5 you'd rather keep the mantisa either between 0 and 1 or between 1 and 10 and you can always do that by adjusting the exponent so in this class our convention will be that the mantisa is always a fraction between say 0 and 1 10th in case of decimal and 0 and half in case of binary in decimal if we write down a fraction the assumption is that there is a decimal point at the beginning and then there are fractional digits so here is the decimal point here's the first decimal digit here's the second decimal digit here's the third decimal digit and so on and the value is what of this thing it's 0 point there so it's b1 over 10 plus b2 over 100 plus b3 over 1000 and so on right this is still the most significant way digit and significance decreases as you go to the right same as before we have just taken an implicit decimal point in front of all the digits in binary if I write dot b1 b2 b3 etc it's the same what I mean is that it's b1 over 2 plus b2 over 4 plus b3 over 8 that's the number so suppose we take our 4-bit system here and pretend that we have a dot in front of everyone then what kind of fractions can we represent 0 is still 0 now our positional values are 1 4 1 8 and 1 16 right so this number is still 0 in decimal or eventually fractions this number is 1 16 only the least significant bit is on and so on things keep increasing until since they're all positive there's no sign yet this number becomes 15 16 we don't have signed now we are really going so if you want to represent binary fractions this is how you do it and the more bits you have here the finer you cut the space instead of values jumping in units of 1 16 if you allocate 8 bits values will jump in fractions of 1 over 256 and so the more bits you add to the mantisa or the fraction the finer grained your separation is going to be between adjacent numbers that you can represent so in computers when we write floating point numbers the mechanism is the same there is a fractional mantisa with a sign and then there's an integral exponent like 11 here in the decimal case there are two primary floating point numbers provided in standard computers one is a 32-bit float and the other is a 64-bit double in a 32-bit float one bit is assigned allocated for the sign there are 8 bits for the exponent which are represented in two's complement manner because the exponent can be both positive and negative and then the mantisa is a binary fraction like I have drawn out on the board there are 23 mantisa bits and 8 exponent bits and one sign in a double meanwhile there is the same one sign bit 11 exponent bits and 52 mantisa bits why this particular division of bits between exponent and mantisa that's an industry standard based on applications that people have found compelling that's how the architecture has been set up and all machines now have this exact same floating point and double floating point representation so that you can freely interchange data between machines there's nothing particularly you know cosmic truth about this if you if somehow in history it turned out there are nine exponent bits and you know 22 mantisa bits that wouldn't over so the universe but it's just a standard that's been set up now if you do the math so one quick envelope calculation that you should know is that suppose I have b-bit number that means you can go up to approximately 2 to the power b now if you want to ask that's roughly equal to 10 to the power what in decimal how many decimal digits do you get if you have b binary bits then you say b log 2 equals b log 10 and so d is equal to b times log 2 to the base 10 which is approximately equal to 0.3 b so if I give you 30 bits you get roughly 10 9 9 to 10 bits of the digits of decimal representation about one third so it's a less than one so if you use that and work out all the details you will see that floats can represent a maximum magnitude of about 3.4 into 10 to the power 38 and a minimum magnitude of 1.4 into 10 to the power minus 45 you should work it out offline based on the representation I have already told you you can do it and 64 bit doubles can represent a maximum value of about 1.8 into 10 to the power 308 and the minimum value of 4.9 into 10 to the power minus 324 now compared to long integers this of course looks impressive but you realize that the number of bits in a long integer and double are exactly the same so the number of distinct real numbers that you can represent will also be about the same there is no free lunch you get the number of bits the number of bits decides how many different combinations you get so in other words finite number of bits can never represent all real values real values on the line are infinitely densely packed and the finite number of bits only gives you a finite number of dots on the line so computers cannot represent most real numbers computers represent only a vanishingly small fraction of real numbers possible even within that range it is measure 0 you cannot represent most real numbers and this leads to a you know a whole field of computer science you need care in writing expressions that combine values to avoid errors and to minimize loss of precision if you are computing the solution of a linear or nonlinear system of equations you are solving a differential equation numerically you are computing the eigenvalues of a matrix numerically you have to take immense care and whether there are multiple PhD thesis written on it which do the calculation in a way that maximally exploits the number of bits available in the machine all through the way until the end in the sense that you do not lose any precision that you can avoid losing so that that's a that's a really difficult science so let's see some of the effect of this apart from having a finite number of bits to represent reals some bit patterns have to be set aside to represent special values for example positive and negative infinity you can easily go and write and we will do so write a C++ program where you divide 5 by 0 dividing 5 by 0 the answer is plus infinity once a value reaches plus infinity if you say subtract 100 from it the answer should still be plus infinity there is a consistent set of axioms for finite precision arithmetic which tells you that infinity minus infinity infinity minus 100 is infinite but how about infinity divided by infinity now you don't know 0 divided 0 or infinity divided by infinity is indeterminate that's not a nice expression to carry around it's not an expression you used to you know aim or rocket or you know pour some chemicals so that's represented by what's called not a number or nan and nan can happen because you divided 0 by 0 or you subtracted infinity from infinity and so on so if you do those dangerous operations the bit pattern of the number is set to this illegal value so that thereafter if you start trying to use this number in combination with other numbers it kind of contaminates all of them and everything becomes now most systems will detect overflow and turn things into infinity properly but most systems will not detect underflow in the following sense so suppose I have a floating point number a which is 3.3 into 10 to the power 38 that's very close to the maximum value of 3.4 into 10 to the power 38 and now I divided by 0.01 this will correctly result in the in a being set to the value called inf inf is a special bit pattern in float and double numbers that the compiler understands so even while printing out that value the runtime library doesn't print a bit pattern it says it's infinity but if I take 3.3 into the power 38 and I add 5 to it so by the way this 3.3 e38 is just 3.3 into 10 to the power 38 it's shorthand from that now if I add just 5 to it if you had to do that calculation manually what would you write so you have 3.3 into 10 to the power 38 plus 5 which is into 10 to the power 0 right the first thing you have to do is line up the exponents so you do that by saying 3 3 and then you keep adding how many approximately 37 zeros right that's the first number which has now been denormalized and then you add the 5 here so you get 3 3 followed by 36 zeros followed by a 5 when you try to normalize that and fit it back into a float you will realize that even the number of mantisa bits you have that 5 will drop off the face of the it will be lost and your answer will still be 3.3 into the power 38 so anytime you add very large and very small numbers whose dynamic range exceeds the number of mantisa bits that are allocated you run the risk of losing precision underflowing the smaller number and normally this may be okay but there are certain calculations in physics and in computer science where I'm actually build up trillions of such terms each term being very small added to a large number so in true infinite precision arithmetic you will get a much larger value than if you keep adding small numbers to large numbers and losing that position so you have to be careful about the order in which you do that so we'll talk about that later when we do a bit more data structure work how do you finally you know declare variables as belonging to types so you say float Fahrenheit that defines the variable called Fahrenheit to be of type float so four bytes are allocated for it somewhere in RAM and initially those bytes have no initialization in C and C plus plus in Java it's guaranteed to be initialized to zero but many C and C plus plus implementations for efficiency will not initialize so if you read you might get garbage or you can declare the variable together with an initialization by saying float Fahrenheit equal to 95 if you know that the value will not be subsequently changed or assigned in your code you might add a notation called constant which means constant here is a floating point value called Fahrenheit which I'm initializing to that value which will never change that has a couple of benefits first of all if you buy mistake try to assign to that value later the compiler will catch you and give you a warning or an error the other advantage is that because it's a constant value it doesn't need to be loaded from RAM if I need that value I can always load it load the constant into a register without consulting RAM and that can speed up my code remember that the number of registers is very small register very fast and they are precious so when I'm computing large arithmetic expressions I often need to flush values from registers back to RAM to make space in the registers if the value is a constant I don't need to flush it back I can just forget it so for all these reasons if you can afford to declare a variable as constant please do so it will speed up your code a little bit and finally you can declare values of variables to depend on other declared values of variables so you can say int x equal to 3 and y equal to x over 2 and by that y will use the value of 3 but 3 divided by 2 in integer is 1 it's not 1.5 so when you write integer divided by integer you mean the lower value it rounds down it takes the floor so why bother to declare at all there are languages where you don't need to declare variables you just pull the variable out of thin air and start using it for this one such language now however it's really bad to coding languages that don't require you to declare variables because what if you type it incorrectly later a single character error in using a variable later on might read garbage for you so declaring is always a good idea you should always use languages which make declaration of variables compulsory and also you can initialize it before any use so the compilers can usually catch if you run a risk of using a variable before declaring and initialize it why do we need to declare that a variable belongs to a particular type one important reason is that many assignments to that variable can be checked for safety if I declare a variable a to be float and then I say a equals hello world that's clearly meaningless and a compiler can catch it then the compiler also can interpret the bit sequence representing the variable as intended in your program if you look at the ram of a machine you have no idea where variables start and where they end they're all amorphous sequence of bits it's only the compiler's internal tables which maintain the sanity of the binding between your logical variable names and what offset in memory holds that value and also what type there see both a float and an inter 32 bits they occupy four consecutive bytes in ram but the mean completely different things in fact the same number 17 written as a floating point number in four bytes looks completely different from what 17 looks as an integer in that same four bytes so your bit representations do not carry type information in themselves the bit patterns in ram become meaningful only in conjunction with a type you start having a value which is meaningful only if you say these four bytes hold an integer for me or they hold a float or part of a character string now as I was saying there are languages that do not enforce variable name or type declaration but that's generally a bad idea with the capital B and the capital I never use a language which is not passive about typing and declaration so now that we have finished the basic floating point and numeric types let's go over how to combine these values into expressions and most of you have you know written enormously complicated arithmetic and algebraic expressions so there's nothing particularly fancy about this just a few rules multiplication is not implicit as you write down on paper you say 5x you have to write 5 times x explicitly because the computer doesn't know otherwise what to do so that's the star and different operators have different precedences some operators bind more tightly than others for example if you wrote the second expression 5 star Fahrenheit minus 32 over 9 star and slash bind stronger than minus this is the binary minus so the second line here would evaluate as the second expression tree here first the variable Fahrenheit would be multiplied by 5 simultaneously 32 would be divided by 9 and then the difference would be taken whereas if you wanted the first expression which is the correct expression for transforming the centigrade then you would have to provide these brackets so that the ordering of operations is clear in this case the first thing that would happen is Fahrenheit minus 32 then you'd multiplied by 5 and finally you divide by 9 totally different expression tree and there are levels of precedence star and slash are at the same level in which case it goes left to right so you can consult there's a link on the resource pages where there is an official C plus plus specification for operator binding strengths and what is computed before what else unless you override with brackets normally you learn the rules of the game the operator precedence game very quickly but while you're coding if you're unsure of the relative importance of binding strength of different operators it's much easier to just put a bracket and forget about it and not worry about using a bracket will not increase the time taken for your code to run it will just make it unambiguous to the compiler so the among the first thing a compiler does on encountering expressions like this is to turn what used to be a string in your input file C plus plus code into an internal representation which looks like this tree so here is another longish expression for the root of a quadratic equation minus B plus in this case plus square root B squared minus 4 AC over 2A and that's the corresponding expression so the compiler scans your source code file and it internally turns it into this expression tree where you compute B squared you compute 4 times A times C you take the difference you invoke a function we'll learn about that soon then you say B and this is a unary minus this is not a binary operator this is a unary operator that actually binds stronger than plus so you first negate B then you add B to that square root meanwhile you do twice a and then finally you divide it now once that expression tree is built now the compiler needs to worry about how to use that finite number of precious registers it has to pull values compute internal nodes in the tree if you run out of registers how to push it back into memory and to plan that so that the movement of data between the registers and the memory is minimized use a small number of registers minimize movement of data between registers and RAM schedule the operations so that if I need a value from memory I'll want to issue that fetch sometime in advance so that I can do more register computations in the meantime as the value is arriving from memory because as I said in the first lecture main memory is much slower than register to register operations so all those optimizations are the subject of compiler design and code optimization in computer science here we are just customers to all the technology finally once you compute that expression you have to assign it to variables and that's pretty easy which is just a LHS or left hand side equals right hand side except that the left hand side has to be a variable name you cannot assign to five that doesn't have any meaning later on when we consider arrays and pointers the left hand side will not be a simple variable name it can be other things we'll see that in due course the right hand side has to be an expression which is compatible with the type of the left hand side it doesn't need to be the same as the left hand side it has to be compatible for example the right hand side may compute the integer five and then assign it to a floating point number or it can compute the floating point value 17.3 and assign it to a float or a double because the double can represent a broader space of numbers than floats which can represent a broader set of numbers than integers and the assignment statement itself as a value which is the same value as it was assigned from the right hand side to the left hand side and this lets you cascade assignment so you can say x equal to y equal to z plus 1 so both x and y get the same value in this case there are exceptions to this when we come to expression operators will look at that now c and c plus plus unfortunately allow some legacy abuse for example you could say x equals y greater than z what does that even mean so this second greater than is a test it compares y and z and if y is larger than z then the value of this expression is 1 otherwise it is 0 so logical or Boolean values are collapsed into integers except that false is 0 and true is 1 or maybe any other non-zero value and that's a problem it's a bug it's not a feature so now let's look at some simple code to do some operations for example I have two floating point numbers 5 and 11 in x and y and I want to swap them so I declared a temporary variable called temporary which I assigned to x and then I assign x to y and then y to temporary and as our time trace I start up with 5 and 11 temporary doesn't exist yet and then temporary is assigned to x so temporary becomes 5 y remains 11 in the next state what x equals y so x becomes 11 y is also 11 temporary is 5 and finally y is assigned to temporary and so changes from 11 to 5 so at this point the values have been swapped so some of you may already know or can figure out how to swap the values without using a temporary variable at all so see if you can figure that out if so mail it to us and we'll comment on it there is one well-known solution but you need to think a little hard about whether it works under all situations okay the next kind of expression we will consider will be logical or Boolean expressions and as I was saying these are 0 1 or true false values which are the results of various checks or comparison between numbers and so on so for example 5 less than 13 is a Boolean expression with value true in C and C++ that true is represented by some integer which is not 0 typically 1 but not necessary 1 e5 greater than equal to 2 e6 is a Boolean expression with value false or 0 if you want to test for equality that is different from assignment assignment is just single equal whereas test for equality it needs some other symbol so that's two equals side by side without a space now in the days before integrated programming environments a very common bug in C or C++ code used to be using equal to equal to when you mean equal and otherwise so it's very easy to create a typo or typing mistake where you mean equal but you type equal equal that's more easy to catch because no assignment is happening much more dangerous is if you're testing for equality but end up assigning instead because the assignment operated also as a value it's very dangerous to replace a double equal by equal some invisible assignment happens and you never know some programmers get into the habit of putting the literal constant on the left hand side of an equal to equal to so that if they miss the double equals you you're apparently assigning to the literal five which the compiler will flag you put a variable on the left hand side you can always assign so in case you're spacing out there if you're if you're comparing you know if five equal to equal to x do something and somehow you miss this equal that's a meaningless statement and the compiler will complain whereas if you say if x equal to five do something okay that's a bug which will not be caught by many compilers it will actually assign five to x the value of the assignment will be five which is not equal to zero therefore it will be considered as true and this will be executed whatever follows here will be and x would have been clovered in the meantime so be very careful with equals and equals equals and then just like complicated arithmetic expressions you can also build up complicated Boolean expressions by combining things so here is a combination expression by first comparing a with b plus 1 and c not equal to d or not e less than equal to f so this is the corresponding expression 3 for that expression so just like in arithmetic expressions there is a precedence ordering between Boolean operators if I have bracketed the meaning is clear so one of the tests is a equal to b plus 1 of course under the b and plus 1 have to be combined and so on so the full expression trees are common is a hybrid between Boolean and arithmetic expressions so here is a place holder which is plus b 1 up to this is an arithmetic expression then this becomes a Boolean expression so I am testing if a is equal to b plus 1 so b and 1 have to be combined through an arithmetic operator called plus that gives me a value which is nameless that is compared with a the result is a true false value which goes up so think of the expression tree as collecting primitive values at the leaves and then passing on more complicated expressions of the tree the next thing I do is ok so there is a choice between and and or and binds stronger than or and will happen first so now I am computing I am comparing c and d to see if they are not equal that is another Boolean expression that is a logical operator these are this is an arithmetic operator that is a logical operator now these two come in into the node called and which is represented as and and ok so and then here is e less than equal to f that is a Boolean operator and then I take these two and then I odd them together ok so in other in human language this is logical or this is and so this is a logical operator right so what how about Boolean algebra itself so Boolean algebra itself most of you are familiar with it at least so there are true or false values to the world of 0 and 1 false and true and there are rules of the game so if I am negating a Boolean value if input is 0 output is 1 input is 1 output is 0 if I am ending together two Boolean variables then the output is 1 only if both the inputs are 1 if I am orring two variables together then the output is 1 if any one of them or both of them is 1 ok so there are many Boolean operators the main ones being not odd and and sometimes xor xor is exclusive or which means that if both of them is off then the output is off if both of them are on the output is off if one is on and the other is off then the output is 1 that is xor so mainly these four Boolean operators cover everything you want and those are expressed in c and c plus plus as the single ampersand the single bar and xor is the carrot operator so there was this expression of resource 1 and so here is e and here is f I am computing if e is less than equal to f and then I am negating it so not of a condition is what it means if the condition is true then the not is false if the condition is false then the not is true ok now of course this is much more easily represented as e greater than f similar to type there is another example which is you can write not e equal to equal to f or you could write e not equal to f so there are a few ways of writing the same logical expression use whatever is convenient in the code so that is what I mean is that clear ok so as I was saying in c you are allowed to write something like if 5 then stuff ok of course if 5 means nothing ok it is rubbish but c allows this by interpreting anything that is not 0 as true if you say if 0 that is 0 is false but if you say if 5 that will execute ok so this is a dangerous legacy and so you should try to avoid it like the plate so do not depend on c or c plus plus using an integer as a Boolean value ok make sure you understand what is going on this is especially troublesome in case you get the precedence order wrong do not put brackets of the right place and you actually get an integer where you are expecting a Boolean or vice versa so check your expressions very carefully for that so let us see if I have a few sample codes to show you let us look at float so here is an example I start with a very large positive value and then I add 100 to it and I print it out and then I take the similar positive value and I divide it by a very small number that should overflow so we will check what happens if you overflow finally I will take b equals a minus 100 I will print b and then I will you know multiply b by minus 2 and print it so let us see what should happen z is very large 100 should under flow you should not see that so z should print out to be exactly you know almost this value and then a should overflow and become infinity b should be infinity minus 100 which is infinity and then by multiplying infinity with minus 2 I should get minus infinity okay and finally I will take a which is supposed to be infinity and divided by minus infinity and see what happens to that I will also write a minus c which is infinity minus minus infinity and see what happens so let us compile this and run that so in the first case I print out g which is the same as the first value here so the 100 dropped off 100 was nowhere to be seen in the second case I divide that large value by 100 and that overflows to infinity in the third case I subtract 100 from infinity and I still get infinity in the fourth case I multiply infinity by minus 2 and I get minus infinity then I divide infinity by minus infinity and I get nan with a minus sign which you may not care about and finally I take infinity and subtract minus infinity and I still get infinity that is because it is infinity minus minus infinity okay if I took a plus c let us see what happens then then we should get none but who knows okay that's not right so that's how floating point numbers work okay so it's 1221 and this time you can get really noisy as if you haven't done enough we'll start with characters next time