 Welcome back to 105. I apologize for not being there today. I pinched a nerve in my back while I was shoveling snow and couldn't walk anymore. And apparently that's just a thing that happens after you reach 25. So you have that to look forward to. All right. So today we get to talk about arithmetic and specifically performing arithmetic in C. So first thing we have to talk about to be very precise is what an expression is. So that's a term that will come up whenever we're talking about the syntax of C. So an expression is just a combination of operands and operators which result in a single value. Fancy was saying something like, I don't know, one plus two. So we know that that will result in three but at the other day that results in a single value. So three or if we wanted to say three by itself is an expression because it's already a single value. All right. So there's all of the operators you would expect. So there's plus, minus, multiplication, division. You've probably already committed this to memory or just habit but there is an order of operations. You might have been taught at bed mass. You might have been taught at head mass. Same thing, just different names. So there's a distinct order of operations we have to follow. So we do everything in brackets first followed by exponents. Then division and multiplication are at the same level and then we have addition and subtraction. And we also work left to right. And if we wanted to be precise about that term that is called being left associative. So it's important to agree on the order of things. So if we have one plus two plus three, left associative means we compute it as, so plus is just a binary operator so we can only do, we can only add two numbers together at once. So in order to compute this we would compute one plus two, get the result three and then add three plus three to get six. If instead it was right associative we do it in a bit of a different order. So we do the two plus three all the way to the right first and we would get five. And then finally we do one plus five to get six. In this case we get the same answer but it's important to always do them in the same order. Question, if I can turn captions on the screen or increase the volume, unfortunately, I can't while I'm doing this, but the volume levels look okay on my end. So hopefully the recording's okay or you can turn it up there. So if we wanted to do something more like one plus two times three, well if we wanted to compute this because we want to do the multiplication first so we say that is higher precedence, then we would compute two times three first to get six and then we add that result to one to get seven. If instead we didn't have precedence we'd get a different order. So we would add one plus two first to get three then multiplied by three to get nine and we would get a different answer so that's why it's important that we agree. For reference, I put on the slide that where all of these precedence rules are in C but we will go over them today and discuss what level each are at. So we also need to talk about division again because in C it behaves a bit differently than what you expect. So if we write out the types of the operands as like int divided by int, the result of doing this division is going to be another int value. So this might not make sense to you integers, they don't have any decimal points or whatever so what's going to happen if the answer should actually have a decimal point? So in C if we compute five divided by two the result is actually two because we can't say 2.5 because remember an integer is just four numbers. Numbers without a decimal point. So the rule in C is that there's no rounding whenever you do division between integers, the result is the quotient that you would get if you did long division by hand or in other words, it would be the proper result minus the decimal place, you just kind of chop it off. So in order to get the remainder in C there's an operator to do that, it's called the module operator and you use it by using the percent sign and that is how you get the remainder from an integer multiplication. It has the same precedence as division and multiplication so it's treated the same but the same but you just get the remainder out of it. So for example, if you did five, mod two is how we say it so here at the bottom five, mod two then the result is going to be one because well, if we did the division by hand it would be we'd get two with one as a remainder so since this is a remainder operator we would get one from doing the remainder while if we just did the integer division we would get two and it's important to note here that both the operands of this module must be int we can't use them with doubles. So C has a few rules for the module operator so the quotient is always truncated so that's the term we use which basically just means take the result and just chop off the decimal point without doing rounding without doing anything special to it. So in order to get the result of the operator the rule for C is that if we can represent the division A divided by B then the result of the integer division so this one will be just another integer just that quotient part or just the result it should be but truncated multiplied by our original B plus the result we get from the remainder should probably equal A in order for math to kind of make sense. So to see what the result would be so I have the columns here so here's A, B and then the integer result of A divided B and then the remainder of A divided B. So if A is five and B is two then the true answer is 2.5 but the integer division would just simply be two and then the remainder would be one. Now we can check that hey this actually makes sense so if we do B times the integer division that's four and then plus the remainder five gets us back to our original A so that is the rule that's going to hold for all of these. So if A is five and B is negative two then A divided B is going to be negative two because the answer should be negative 2.5 and then the modulo is going to be one because if we multiply B which is negative two by the result of the integer division which is negative two we get four so in order for this equation to make sense well the remainder has to be one that gets us back up to A which is five because it always needs to equal A. Gets a bit weird whenever we have this case where the dividend or A in this case is negative so if A is negative five and B is two while the integer division is still going to be negative two because we do the truncation of negative 2.5 but now in this case we get a negative mod which is a bit weird but makes sense in our equation because two times negative two is negative four so in order to get to A which is negative five we need it to be negative one. So it's a bit weird. People don't really expect sometimes that the remainder can be negative so this is just a warning to look out for that that it may be negative. I believe in this course whenever we take the modulo operator we assume all of our arguments are positive so if A and B are positive and you take the mod of that the mod is also going to be positive but just in case you could get a negative number there just watch out for that because the module might be negative. All right the other part of that rule was oh okay this equation holds if we can represent A divided by B. Well why can't we represent A divided B and the main thing we can't represent is division by zero so in math that's impossible. If you write it in C you can write one divided by zero you can compile it it'll give you a value and let's go ahead and do that to show that well it's a bad idea. So here is my program here and this is going to be a bit different. I noted from what you might see in Visual Studio Code because I use a different extension so this format is actually just telling me whoops telling me the name of the argument for the printf function but it's not actually used. So if I look at this my compiler gives me a little bit of a warning here it says division by zero is undefined which we will see that if your compiler ever says something you're doing is undefined then you should probably not do that thing. So in this case I am dividing by zero. So if I go ahead and try and run this program I get a negative some weird number and I'm going to get a random number every time because it's undefined which just means your computer can just spit out any old random number and it doesn't actually make sense and it's not guaranteed to make sense. So if I run it again I get a completely different number if I run it again different number so on and so forth. Every time I run it I get a different number. So the value we got back there was undefined could be different every time could be different on different computers yours could do something different than mine yours could have a consistent result mine could be inconsistent undefined means you can't actually rely on the behavior which is a big source for errors if you rely on something that's undefined and it just happens to work for you and then you try it on some other computer it suddenly won't work anymore and it will be very hard to figure out why and might be because you have undefined behavior. So you can see this is undefined behavior, US spelling sometimes you'll see it pop up as just UB and it is one of the harder types of problems to debug that you should avoid at all costs. So once we get through the course a little later whenever you do division you should make sure that the number you are dividing by is not zero if it is zero then big problems are going to happen so you should make sure you do not divide by zero. Another question might be I don't see any questions yet hopefully things are working so another question might be what happens if we mix types so we know we have ints and we have doubles what happens if you kind of mix them together so the operators plus minus multiplied divide and modulo they're all binary operators so they only take two operands and then they themselves are the operators so what the rule is is if both of the operands are ints then the results is an int and recall for modulo both operands have to be int otherwise it's not going to work but the other rule is if at least one of the operands is a double the other is going to be converted to double if it's not already one so when we convert an int to a double all we do is add .0 so we just add a decimal point and then a zero so if we for some reason write in our C program 2.5 plus 2 well we're not allowed to just add a double and an int so C has to convert this two to a double so this two which is an int gets converted to 2.0 which is a double and then it would do the addition so the result would be 4.5 so that automatic type conversion you'll see it come up it's called implicit type conversion implicit just means we didn't request it like we didn't actually tell C to do it it just kind of did it because it was following its rules so it basically means we didn't actually request it and it might not be obvious that we requested it so if we go the other way so if we convert a double to an int it does that truncation like when we had when we were doing the integer division so that just means it will just chop off anything after the decimal point it doesn't round it doesn't do anything like that so we can do explicit type conversion if we want with something called a typecast and a typecast is a unary operator so it only acts on one operand likely a variable or a value and it has the form so it has parentheses and then you say the type which will probably be either an int or a double and then the value or the variable that you want to convert to that type so this has higher precedence than the binary operator so it will always happen first and it is right associative with itself so if we have multiple it will start from the right and then go to the left because well it only has one argument here and the thing the operator is on the left so what would that look like so just to show you what that would look like for division is if I have int x equals 3 divided by 2 let's see if people are around in discord so what should the result of this be so if I do the integer division of 3 divided by 2 let's see I'll give it a minute see if anyone is typing it on discord oh I see one person typing so let's see if we can figure out what the integer division is so one we got ones a bunch of ones all right good we got a whole bunch of ones so in this case yeah it would be one so what is the result of this is one so what would the result of 3.0 divided by 2.0 be so we got ones oh we got 1.5 okay so yeah this would be 1.5 and that is also why here on the first line with the int that's the integer division so the result should be 1.5 but because it's integer division it gets truncated so it's just a 1 and not a 2 so we have this double and then this is 1.5 so if we go ahead and we run this program hopefully we get the results we expect oops it is called division and we get yeah x is 1 and then y is 1.5 and this would be true even if we change this 2.0 to a 2 so even if we do that our result is going to be 1.5 because this 2 gets converted to a double so it'll be 2.0 and then we'll have what we had before so if we just compile that and run it again we'll see why is still 1.5 if I change this back to 2.0 and then remove the 0.0 from the 3 shouldn't matter which operand is which this 3 should then get converted to a double and then we should get our final result which would be 1.5 again so if I compile that I get 1.5 I only get the integer division in the case that they are both integers and in this case it will I get a nice little compiler warning that says the result of an integer division is used in a floating point context what that means is the result of this the result of this expression is going to be 1 which is an int and then I'm assigning it to a double so if I'm assigning it to a double I'm converting it to an int to a double afterwards so the result the value of y in this case would just be 1.0 so I will compile that and run it in this case I get 1.0 for y because it's still a double if I took the result of the integer division and then it implicitly converted it back to a double my compiler was trying to tell me I did something silly so that's what that means but if I change either one of them to a double then I am good or I could do that typecast we just learned so if I did that then I would convert this 3 to a double first so it would be the same as doing if I added more brackets it would be the same as doing this first so I convert the 3 to a double so it's 3.0 and then if I do the division then it's 3.0 divided by 2 and then that 2 will be converted to a double and I'll get 1.5 at the end of it all right we have a question was the correct way to tell the compiler that the integer truncation was intended so in this case if I really wanted to mean like that integer division I should assign it to an int like I did above for x otherwise if you sign it to a double it thinks you're doing it thinks it's a problem and yeah another question why am I using return success instead of return zero so we went over that at the end of last lecture so in the standard library generally it's not a good idea to use magic values generally you want them to mean something so it's a bit easier to read so in this standard library header there is something called exit success which you should use to return from main if your program has completed successfully so turns out that exit success is simply just zero but it conveys our meaning a bit better so it might be a bit easier to read all right I think that's so yeah so we don't have to use the typecast on the two in this example because after the typecast for the double to three the three is now what it would do is just replace this three by three point zero and now we don't have to cast two because it's going to be doing a double divided by an int so it would implicitly go ahead and convert this two to a two point zero and then they'd both be doubles and we get one point five right let us switch back quickly and we'll have some other examples and I think we'll have time at the end so we can mess with some mess with some programs since we know how to write some programs now so oops wrong one so here are some of the rules again so if we did int two point five we're converting it from a double to an int so it just gets truncated so the result of int two point five would just be two and now that is an int like I had in the example if we had double five divided by two gets computed right remember that the type conversion has a higher precedence than division so we would do that first so it gets computed as double five first so we convert that int five to a double which would be five point zero so after that first step it would be five point zero divide by two so this two would get converted implicitly to a double so it would be two point zero and then we get five point zero divide by two point zero which is two point five and finally if you did something silly like this so you did double int and then a number well because the associativity is right to left we would do everything to the right first so we would convert this two point nine to an int first so this the result of this operator right here would be the integer two because it gets truncated and then we take that two and then we convert it to a double so if we take convert a two to a double we just get two point zero so that's another way to do some operator on a double if you want that's just a way to truncate a double but it looks a bit ugly turns out we'll see in the next lecture a nicer way to do this but it is one way to truncate a double so another thing is the assignment is also technically an operator we saw that before whenever we declared a value we could assign to it and then on other lines we could just assign new values to that variable well turns out assignment is also an operator that you can use in expressions but it's a kind of a bad idea to use it but if you wanted to know the binary operator is right associative and it's the lowest precedence of anything we've seen so far so the result of an assignment operator is the value assigned so if we wrote x equals 3 then the result of that is going to be 3 this means you can write something like x or y equals x equals 3 so because it is right associative it's going to get computed as this so the x equals 3 is going to happen first so we'll update x the value of x to 3 and then the result of this is a 3 so after that first step we'll get y equals to 3 and then we would just assign the value 3 to y so it lets us do that for now I would warn you against doing things like that so I would instead just do this on two separate lines because generally especially when you're starting doing assignments as part of expressions is a bad idea so trust me on this one so there's some other shorthand operators are that you can use and there's a question about python I can answer python things afterwards because not everyone might know python so there's some other shorthand assignment operators you want you may use so you might find yourself doing something like x equals x times 2 if you just want to double the value of x so you updated it so that after you do the statement then x is twice the value it was before so c has a shorthand and other languages have a shorthand for things like this so instead of this I can just write x multiply equal 2 and they actually just mean the exact same thing you just save yourself repeating x twice so you just say it once so this applies the operation to the value of the left of the assignment and then reassigns it with the result so this will be 2 times or sorry x times 2 and then that result would be assigned to x so it's just a shorthand way of doing things then we have a question what if I add a double to x that's declared as an int so in that case in that case the sorry the addition or multiplication or whatever would be done as doubles and then to fit it back into that variable which would be an int that would be an in plus a type conversion and it would get truncated again and then another question are shorthand assignments considered expressions that can be used in part of other expressions and in C yes they are but again I would I would tell you to not use them if you do any assignments you should just have one assignment per line and that's it and we'll see once we get into the course there's some annoying operators that look really close that do something completely different so there's shorthands for all the binary operators so I can do plus equals I can do minus equals I can do multiply equals divide equals mod equals if I want so there's shorthands for all of these there's also some more shorthands for adding and subtracting by one so this is a really common operation in programming and for computers in general so much so that while we give it special names so adding one to a variable is called incrementing so it just we increment it by one subtracting one from a value is called decrementing you're going to hear these terms come up a lot so there are special operators for incrementing and decrementing so the unary operator it's a unary operator so it just increments or decrements a value for incrementing it's plus plus and then for decrementing it is minus minus so annoyingly there's two versions of this operator so it can either come before the variable which is called prefix or it could also come after the variable which we call postfix so pre is before post is after so it can either come before or after the variable so why do we have two versions of this increment and decrement well they do slightly different things the difference between the two is actually the result of the operation so if we assume initially we have we declared an x and it equals zero if we did plus plus x well that will increment the value of x so it will add one to x and the result is going to be the updated value so the result of x plus plus plus x is going to be one the other version of it the postfix if we have x plus plus that still adds one to x but annoyingly well not annoyingly sometimes it's useful but probably not for us the result of this expression is the original value so in this case the result of plus plus x is actually zero so it was the value before we actually added one to it so in both cases x gets reassigned the value of one it's just the only difference between postfix and prefix is the result of this and there's a slight typo here because this should be x plus plus so I apologize for that typo I will fix it right after that so the rule is you should always prefer the prefix so this version that always returns the updated value unless it's strictly necessary and you're doing it but generally if you actually need it you're being too clever and the compiler is more clever than you so you should just write something that's more clear and easy to read so when you go back to it you actually have an idea of what you meant without remembering these rules so remembering these rules generally isn't really worth it you should just know that plus plus just increments a value and you should always just use plus plus x instead of x plus plus so and then question can you give an example of using both so we can see a quick do we have an example ready so in this case we don't know we don't have any good way of knowing when we would use one or the other but we could do something like this just to show that I'm not really lying to you so here's a program we just have a int x and then we have a print f with a format string we're just going to print a integer so in this case if I print plus plus x then I compile it and then I run it then the result is going to be one because this is the prefix version of it which always returns the updated value if instead I did plus plus x plus plus then oops then I would get just zero and then we have another question what about the order of operations if we did something like if we just sandwich it if we just sandwich x we actually just get a error that this expression is not assignable so we actually cannot do something like plus plus x plus plus because well if you go over the full precedence rules it will actually do the post fix first again you don't really have to know this but just just for illustration so if it did this part first whoops if we did x plus plus first then the result would be a zero and then we can't do plus plus zero that doesn't make sense you can only change a variable so I can't do plus plus x or anything like that and then yeah another question what if I print x twice so just to show that we actually updated the value of x let's have two lines here so I'm going to print the result of x plus plus which will result in the result of this will be zero but the actual value of x is going to be updated to one and to show that I will just print it again so in this case if it was actually updated then when I run this I should see the result of zero for the first line and then the result of one so that's just to show you that hey it's actually updated here which again is a bit weird generally always prefer plus plus x to x plus plus there's also other reasons to prefer the prefix version of it turns out in order to get the original value the compiler might have to do something that is slow in order to do that even if you don't really mean it and it doesn't have and it doesn't need to do that so you're kind of getting in the way of the compiler generating code as well but that's an argument you can make in like a fourth year course but just no prefix version and you will be okay so there's another unary operator called size of and that tells you the number of bytes used by a type or a variable so the result of size of is the number of bytes used and it's just an integer in fact it's always a positive integer so we can use this to verify the number of bytes used for some types so if we do size of int well the result of that is going to be four because it takes up four bytes so if you don't remember the size of a type then you can always just ask C and print it out yourself so the result of size of double that's eight bytes size of char is one so one byte and a bool is also one byte because well it's a whole number we can't actually do anything smaller than a byte it's typically the smallest unit we use in computing whatever we have to deal with memory smallest unit would of course be a single bit a one or a zero but we can't really do much with it so again you can also use size of on variables so if I declare a double x I can also do size of x and I can get the result of eight because well x is a double and it consumes eight bytes so here's all of the summary for the precedence rules for today's operators just spelled out you don't have to remember all of them so these are all the unary operators at the top that just take a single value so those have the higher precedence so you don't have to memorize all of them just just know that unary operators have the highest precedence so you would do those first so in this case here's our typecast and then our operators the only difference is the associativity so in postfix we have to go left to right because the operator is on the right for all the other unary operators the operator is actually on the left so the associativity is right to left so typecast is one we saw before that the ampersand to get the address of is actually a is actually a unary operator as well size of is a unary operator and then here is where we have our normal math so here's multiplication, division, and mod and then here is addition and subtraction and they are left to right associative and well the things near the top are higher precedence and at the bottom is lower precedence so we would do those last so addition, subtraction and then what has the lowest precedence is the assignment statements which are right to left associative but again I do not recommend you try to mix them in expressions you should just have them on a line so just if you want to assign something just have variable equal or variable plus equals and then don't do any assignments after that just do one assignment so other things we can do in C just to make things a bit more readable is you can add comments to your code and it is basically a note for others to read the compiler completely ignores them so what the rules are for them is any text you write between a slash star and then a star slash is considered a comment that the compiler ignores so it won't consider it as code it's just messages to write to yourself you can also use just slash slash and then anything after that's ignored until a new line so until you hit enter so for example if you wanted to leave yourself a note as to why I included std lib well you could write to the right of it oh start a comment say I need this for exit success so that's why I have it here otherwise if I just return zero I don't actually need this line and I could delete it so maybe that's a note to yourself that if you change this to return zero again you can go ahead and delete this line you can also just add a note to yourself at the beginning of main it says oh the program will start here and it will finish when it hits return so if you want to leave notes to yourself you can do that question about how much are you being graded for comments on the labs for the most part your labs are all auto graded if you just submit them and you get a grade back and there's no TA sitting down with you marking it comments don't count for anything you're not graded for comments but it's a good idea to have them so if you refer back you actually know what you're doing or if it takes you more than like if you take more than a day to finish your assignment generally it's good to leave yourself comments so you know where you were and where to pick up from and then resume your work so another thing to be careful of is just be careful of truncated integer division so if I asked any normal person what is one divided by two plus one divided by two what would the answer be so assume you haven't taken this course yet tell me what one divided by two plus one divided by two is oh well it's on the slide already so the answer should be one makes sense right so what would it be in C so I will leave this to you now so if I did one divided by two plus one divided by two what would it be in C yeah I've seen the answer already big ol goose egg big zero so someone that is not familiar with C and doesn't know about truncated integer division they would see this and maybe read a zero in their program later and be like what the hell does C not know how to do math what a stupid programming language this is well it's actually because of that truncated integer division and if we apply all of our rules today since division has higher presence than add or than addition when we compute this we would do the division first and since one divided by two they're both integers well we truncated so the answer should be 0.5 but because we're doing it on integers we truncated so we just chop off everything after the decimal point so the result of this is actually going to be zero and the result of this division is going to be zero as well so so we would get zero plus zero which is simply just zero so we have a few more minutes for questions so any questions from today or anything you want me to do and explain this is generally the thing that trips up people the most especially when you read the program and it's like well obviously that's one but hey no it's not it's zero so I will give it let's give it a few minutes you can type your question in the discord chat I see some people typing right now oh so question can we do arithmetic on booleans so false plus false equals false we can try it out generally doing arithmetic on booleans doesn't really have any meaning we will see in the next lecture no in two lectures we'll see what we can do with booleans right now booleans aren't actually that useful so if we can do false plus false so sure we can do false plus false and if we do false plus false the result is zero because false is just another word for zero if we did true plus false again probably doesn't have much meaning but true is equal to one so true plus false should probably just equal to one so you can do you can do it on booleans generally it doesn't mean anything but sure you can see lets you do some weird things even if you as a functioning human would read it and be like why are you doing that that doesn't make sense all right any other questions we have a question about strings so we will not we're going to talk about strings much later in the course so don't worry about strings and then another question hey if we add two characters oh will that add their ascii value so yeah so if we do char c equals capital A which is what 65 I believe well if you for some reason add let's say we add x plus c what it's going to do is it's going to a char c it's just a byte so it's just going to consider it as an integer and then just do the addition so this would be integer division so in this case I believe capital A is 65 so if we compile this and look the result should be 66 because x is one and then c is 65 and then yeah someone's asking if we can just do a plus true and in this case we can we're going to get the same thing we're going to get 66 again probably doesn't really have any meaning to us whatever you write programs you should write them in a way that they are readable and make sense there is a branch of programming where you try and write the most confusing program possible to just I don't know try and overwhelm whoever is trying to read it and figure out what your program is actually doing but in this course we are going for clear programs that don't have any additional things like everything actually has a purpose and a meaning so any other quick questions before we wrap up and I apologize if you can hear my dog there is someone at the door and she is very alarmed and then a question is can I cast an integer to a char so you can cast an integer to a char but remember that a char is only one byte and an integer is four so if it's a number it can't actually represent it will behave it will actually try and convert it into the smaller value and it'll do something similar to what we did in the last lecture when we had an int and then we tried to assign it a value that was too big for it so the same thing is going to happen for characters but it will try generally if you know that for sure your character will fit in an int then it is perfectly fine to do that so in fact if I made instead int C and then I took that char and converted it to an int well that would be fine because it would just convert A so the actual ASCII value and here if I look over in this it actually tells me with this extension I don't think yours may not do that but says hey it's 65 so that 65 which just fits in a byte would get converted to an int so now it would just take up four bytes and now we can just use it and then a number one another question is if I just do char what was it char A int A so something like this would also work but my compiler oh yeah something like this would also work so it would take A convert it to an int so now it says 65 but takes up four bytes and then at the end here would be an implicit conversion to fit it back into a char so it fits in C and in this case while it's 65 so the integer 65 can actually fit all right with that we are out of time so just remember I'm pulling for you we're all this together