In this lesson, we will discuss arithmetic operators, which entail nothing more than common math calculations. There are a few other “fancier” math operations available, but we will simply cover operations most commonly used in business applications:
For this lesson, you can either use the "Before" code provided and skip ahead, or create an ASP.NET application called “CS-ASP_009” using the previously detailed workflow. Add to this Project a Default.aspx file with the following Server Controls and programmatic IDs:
TextBox - inputTextBox
Button - okButton
Label - resultLabel
Before going into math operations, it is important to distinguish the way you would normally think of the equal’s sign (=) with the way it is used in C#. In math, the equal’s sign is used to designate equivalence between elements on both sides of that symbol: 1 + 1 = 2
However, in C#, the equal’s sign is not used as an equivalence operator (there is another operator used for that operation). Rather, the equals sign designates assignment. This was touched upon in the previous lessons where it was mentioned that dumping the contents of one variable “bucket” into another assigns that value to it (int i = double j). Value assignment can be of any data type available, not just numerical values. For instance, a bool could be assigned the value of another bool variable using the equal's sign.
With this in mind, let’s write out some basic math operations using familiar mathematical operators, then assign the results of those calculations to a variable “bucket”. You can write this out in the okButton_Click event in Default.aspx.cs.
Copy and paste the int result line four times, but change the mathematical operation each time. Then, comment out the previous "result" variable.
It might be helpful to read these statements from right-to-left. In other words, i + j is calculated first, then stored inside the variable on the left side of the equal's sign.
You can treat the variable used in the calculation as the literal value that it holds. That means that there is no essential difference between the formulations shown above, and one such as this:
You can also take whatever current value is contained inside a variable, perform an operation on that, and store it back to the original variable all in the same line of code:
There is a shortcut when performing this calculation, by combining the arithmetic operator with the assignment operator as follows:
The above example has the effect of incrementing by 1. In other words, it is shorthand for adding 1 to the current value of the variable. You have yet another shortcut for representing a single increment by writing this:
Or, you could decrement in much the same way:
Note that you can perform these operations, one after the other, as much as you want:
You can use the ++ and -- to decrement only by a value of 1, whereas with += and -=, you can increment or decrement by any value you choose, including another variable. Depending on the situation, one method may be more effective than another.
When performing calculations, it is important to be aware of operator precedence (also called “order of operation”, remember My Dear Aunt Sally?). For example, what do you think the result will be when running this code?
The answer is 12 because C# adheres to the operator precedence dictated by ordinary math. In other words, multiplication and division occur before addition and subtraction. However, what if we intended to first add 5 + 1, and then multiply the result of that with 7? You can create your own precedence by wrapping the calculation in parentheses, again, just like in ordinary math:
We get this result because we instructed Visual Studio to perform the operation inside the parentheses (in this case, addition) first, then follow order of operations as normal.
Next, let’s see what happens when we mix a decimal value with a whole number in a calculation:
The calculation performs as we would expect because myInteger gets implicitly upcast to a double in order to make the calculation work, then the answer is stored in a double. However, let’s see what happens when we try to store this calculation to an int instead:
As you can see with the red squiggly underline, this produces compilation error immediately recognized by the compiler. It warns us that we are trying to implicitly convert myDouble to an int, however, we know that we can do the conversion ourselves by simply casting as follows:
This now works because we are downcasting myDouble to an int, but as you may recall from the previous lesson, this will result in truncating the decimal data:
You should always be aware of truncation occurring when dealing with integers, especially when dividing whole numbers and wanting a decimal value result:
When dealing with doubles we would expect the result of this division to be 1.75. However, because we are dealing with integers, the resulting calculation ignores the decimal data altogether, leaving us with 1:
You may be tempted to solve this problem by storing the result of the calculation into a double instead:
However, this still yields the same truncated result as before. The reason for this is because the calculation is still being performed on integers, and the result of the calculation will always be a whole number. Yes, the result is being stored as a double, but the calculation is being performed on ints, which must be whole numbers. After the calculation, the result assigned to the double. The way we fix this is by upcasting the ints to doubles:
In the previous lesson, we confronted the possibility of data overflow, where a given data type isn’t large enough to store all of the information it is given. Recalling that an int is only capable of storing a number up to 2147483647, let’s see what happens when we overflow an int variable with a number that is too large:
You are probably quite confused by this result. While it is clear that the result of this calculation would be way outside the boundaries of what an int can store (2,147,000,000), why would it result in a negative value? The answer is quite technical and not necessarily helpful to a beginning programmer. Just be aware that this can happen when doing calculations that may involve larger numbers than you anticipated.
The moral of the story is that you should always use larger data types whenever you're performing arithmetic operations with the potential to overflow. It's best to assume that an error may occur and prepare for that in order to ensure your code will successfully run.
The most obvious way of solving this problem is to use the largest data type we know of for storing such a large number:
If you only cast the assigned-to variable (resultNumber) to a long, you will get the overflow, for a similar reason as we saw with truncation by dividing ints earlier, where the calculation is being performed on ints:
Perhaps the most important thing to realize about these kinds of overflow problems is that they can happen quietly, without your knowledge, unless you take steps to:
Consider the possibility of it happening ahead of time
Use a “checking” measure to report when an overflow occurs
There is a built-in checking measure that you can use in C# called checked:
When the application runs, it will no longer be silent about the overflow. Instead, it will crash on this line of code, delivering an OverflowException:
Note: While having an exception occur might seem worse than simply letting the code run, it isn't. Programmers can respond to exceptions and keep record of them, as opposed to quietly letting the code deliver an incorrect result to the end-user. You'll find that exceptions are a tool, not a hindrance.
Don’t worry about how odd this code might look right now. This will become clearer when you learn about scope in a later lesson. This is just an illustration of the kinds of issues you can run into when you are not careful about properly handling your data types. In the end, no programmer can rely on foresight alone; apps will crash, users will submit bug reports, and mistakes will need to be weeded out accordingly. However, what programmers can do is account for these possibilities, and write safeguards to gracefully handle such occurrences.
There is more to arithmetic operations than what was covered in this lesson. However, with these basics under your belt, you will be well-equipped to tackle the majority of problems needing to be solved when working with numerical data types in the coming lessons. Great job, keep it up!
Lesson 9 - Arithmetic Operators