Skip to the content of the web site.

Lesson 1.5: Arithmetic operators (binary and unary)

Previous lesson Next lesson


Up to this point, you have seen

	std::cout << "Hello world!";
	std::cout << std::endl;

where there appears to be some relationship between the << and what is executed. We will now try to make sense of this:

In C++, there are two ways of performing an operation:

  1. by using an operator, or
  2. by calling a function.

We will discuss functions later, but they are similar to functions you have already seen: $\sin(x)$, $\cos(x)$, $\ln(x)$, $\max(x, y)$ and $\gcd(m, n)$ (the last denotes the greatest common divisor of $m$ and $n$). That is, they take arguments and they evaluate to a result. For example, $\gcd(4620, 11466)$ evaluates to the result $42$.

We have already discussed a few arithmetic operators in C++

OperatorDescriptionInteger
example
Integer
result
Floating-point
example
Floating-point
result
+addition 33 + 9423.3 + 0.94.2
-subtraction 81 - 18638.1 - 1.86.3
*multiplication6*91200.6*0.91.2
/division 15/351.5/3.00.5

You will note that all of these operators take two operands, and thus they are call binary operators. This is the second use of the word binary and it means something completely different. In the first case, we referred to a binary number system, a term that is often abbreviated to just binary. In this case, the word binary emphasizes that the operators take exactly two operands. Later, we will see that there are unary operators (operators that take only one argument, like the factorial operator from secondary school; e.g., $3!$) and even one ternary operator, an operator that takes three arguments; however these next few topics will focus on binary operators.

Definition: binary operator
An operator that takes two operands to get a result. The operator is always written between the operands.

All of these are binary operators, for they all take two operands ($37 + 52$, $20 - 5$, etc.) and return a result.

Order of operations

C++ uses order of operations, so you will need parentheses, just like in your mathematics class:

	std::cout << "3 + 4 * 5 = ";
	std::cout << (3 + 4 * 5);
	std::cout << std::endl;

	std::cout << "12 / 3 + 4 * 5 - 12 = ";
	std::cout << (12 / 3 + 4 * 5 - 12);
	std::cout << std::endl;

	std::cout << "12 / 3 + 4 * (5 - 12) = ";
	std::cout << (12 / 3 + 4 * (5 - 12));
	std::cout << std::endl;

You can run this on repl.it.

In the first std::cout statement, the mathematical expression appears within a string, so it is simply printed as is. In the next statement, the expression is part of the C++ statement, so it is evaluated first before it is printed.

In your mathematics courses, you may have used different delimiters, including brackets and braces. In C++, however, the only delimiter available for grouping arithmetic operators are parentheses.

Try these out at this point by replacing or adding them into your existing C++ code.

There are many other binary operators in C++, but we will only look at a few more. All the previous operators are called arithmetic operators because they perform arithmetic. Programs often require logic, as well, and thus there are logical operators.

Definition: binary arithmetic operator
An operator that takes two numbers as operands and returns a number depending on the values of the operands. Most programming languages use +, -, * and /. Most programming languages do not have an operator to represent exponentiation.

Integer division

If you divide one integer by a another, the result may or may not be an integer. The question is, what do you do if the division of one integer by another does not result in an integral value? If you execute the following code, you will notice the results may not be obvious:

#include <iostream>

int main();

int main() {
	std::cout << "3/4 = ";
	std::cout << (3/4);
	std::cout << std::endl;

	std::cout << "13/4 = ";
	std::cout << (13/4);
	std::cout << std::endl;

	std::cout << "43/4 = ";
	std::cout << (43/4);
	std::cout << std::endl;

	std::cout << "-43/4 = ";
	std::cout << (-43/4);
	std::cout << std::endl;

	return 0;
}

You can run a similar program online.

Executing this always produces an integer. This is because the integer division gives the integer quotient: for example, if you divide 5228 by 19, using long division, continue so long as the quotient is an integer:

This indicates that $\frac{5228}{19} = 275 + \frac{3}{19}$. Integer division keeps the integer quotient and discards the remainder, so when you attempt to calculate an integer division, the result will always be another integer:

#include <iostream>

int main();

int main() {
	std::cout << "5228 / 19 = ";
	std::cout << (5228/19);
	std::cout << std::endl;

	return 0;
}

this prints 275 and stores the integer value 275.

In general, if m/n is calculated, it will give a quotient such that n*(m/n) is less than or equal to m, and greater than m - n.

Please note, the C++ compiler does not determine short cuts or simplifications: if you attempt to execute the following program, you will see that the compiler does not cancel out the $n$s:

#include <iostream>

int main();

int main() {
	std::cout << (2*(13/2));
	std::cout << std::endl;
	std::cout << (3*(13/3));
	std::cout << std::endl;
	std::cout << (4*(13/4));
	std::cout << std::endl;
	std::cout << (5*(13/5));
	std::cout << std::endl;
	std::cout << (6*(13/6));
	std::cout << std::endl;
	std::cout << (7*(13/7));
	std::cout << std::endl;
	std::cout << (8*(13/8));
	std::cout << std::endl;

	return 0;
}

You can run this program online.

Here is a more humorous example for determining the order of operations in C++.

Modulus operator (the remainder)

When you divide $35$ by $8$, you will find that $35 = 4 \times 8 + 3$. The $3$ is said to be the remainder of this integer division, and we may now write $\frac{35}{8} = 4 + \frac{3}{8}$. The remainder will always be an integer from $0$ up to but not including the divisor (in this case, $8$).

In mathematics, it is common to use the binary operator ${\rm mod}$ to indicate the remainder, so $35\ {\rm mod}\ 8 = 3$ and $42\ {\rm mod}\ 9 = 6$. The following statement is always true:

Theorem
An integer $m$ is an integer multiple of $n$ if $m\ {\rm mod}\ n = 0$.

We may therefore make some further observations:

Theorem
An integer $m$ is even if and only if $m\ {\rm mod}\ 2 = 0$.
An integer $m$ is odd if and only if $m\ {\rm mod}\ 2 = 1$.

Important concept:
The operators used in mathematics need not necessarily be the same as the operators used in a programming language, and not all programming languages will use the same operators to represent the same operations. Thus, it is always important to learn and use the mathematical operation when describing a program, and we only translate this into a specific programming language.

In C++, to get the remainder of an integer division, you must use the modulus operator %:

#include <iostream>

int main();

int main() {
	std::cout << "35 % 8 = ";
	std::cout << (35 % 8);
	std::cout << "42 % 9 = ";
	std::cout << (42 % 9);
	std::cout << "5232 % 19 = ";
	std::cout << (5232 % 19);

	return 0;
}

This will print the values 3, 6 and 7.

In C++, the modulus operator % always returns that value such that

                        n == ((n/m)*m + (n % m))

So, if m and n both have the same sign, the modulus must be a value between 0 and m - 1, while if m and n have opposite signs, the modulus must be a value between -abs(m - 1) and 0.

Spacing around operators

Note that this author prefers to always place spaces around + and -, and not place any spaces around * and / to emphasize the order of operations:

	3*4 + 5*(6 - 7 + 18*3) - 26/3

Just from the visual appearance, the order of operations more clear than either of

	3 * 4 + 5* (6 - 7 + 18 * 3) - 26 / 3
	3*4+5*(6-7+18*3)-26/3

Important concept:
In C++, you can put as many spaces or even new lines between operators and their operands. It is critical that you learn early on to put spaces between operators and operands to ensure that your code is easy to read.

	std::cout <<
		"The result of evaluating the expression ";

	std::cout << (
		3 + 4*5-6  /   7 
-8          *                 9+42 +                     (
                   23+65
*5)

);

	std::cout << " is the same as ";
	std::cout <<(3 + 4*5 - 6/7 - 8*9 + 42 + (23 + 65*5));

Now is the time to learn good coding style—right from the very start! As an engineer, you will be responsible not only for developing code, but you will also be responsible for maintaining code. Suppose the above code contained an error: the requirements for what you were developing needed that 7 - 8 be evaluated first before either the surrounding division or multiplication. In the second example, the spacing itself emphasizes the order of operations, and a programmer could detect the error and correct it to read:

	std::cout << (3 + 4*5 - 6/(7 - 8)*9 + 42 + (23 + 65*5));

Remember that the time your developers spend on trying to understand code is a cost, and engineers are always interested in reducing costs. While this author has never seen code as egregious as what is presented above, he has seen a lot of very poorly written code (and no doubt has, on occasion, also authored very poorly written code). The earlier you start avoiding poor programming practices and adopting good programming practices, the better off you will be.

Upcasting

Suppose you have an operator where one operand is an integer and the other is a floating-point number:

	std::cout << (1.0/2);
	std::cout << std::endl;
	std::cout << (1/2.0);
	std::cout << std::endl;

What does the compiler do? Because it is always possible to interpret an integer as a floating-point number (e.g., 7 has the same value as 7.0, the integer is converted to a floating-point number. Thus, the above will make the calculation 1.0/2.0, which will evaluate to 0.5.

Now, what happens if we have:

	std::cout << ((3/2)*2.5);
	std::cout << std::endl;

The result may surprise you, but it is quite reasonable. You may think that because the result of 3/2 is multiplied by 1.0, everything would be converted to a floating-point number, but the compiler will make these conversions only when necessary:

  1. First, the compiler sees (3/2), and as both operands are literal integers, integer division will be used, resulting in a value of 1. This now will be multiplied by 2.5, and so the integer value 1 will be converted to a floating point number, and 1.0*2.5 evaluates to 2.5, which is then displayed to the screen.

If, on the other hand, you had

	std::cout << (((3/2.0)*5)/2);
	std::cout << std::endl;

the compiler would see that the second operand of 3/2.0 is a floating-point literal, so it will calculate 3.0/2.0 which equals 1.5. Thus, the next calculation is 1.5*5, so the 5 is converted to a floating-point number and 1.5*5.0 evaluates to 7.5. Finally, the last calculation is 7.5/2, and so the 2 is converted to a floating-point number and 7.5/2.0 evaluates to 3.75.

Definition: integer and floating-point operations
Computers have an arithmetic-logic unit (ALU) that can calculate integer arithmetic operations, and a floating-point unit (FPU) that can calculate floating-point operations. There is no circuitry for calculating the sum of an integer and a floating-point number, so the integer must first be converted to a floating-point number and then both operands are sent to the FPU.

Examples of upcasting can be seen here on repl.it.

Unary operators

You have seen operators in your mathematics course that take only one argument: $-n$ and $n!$. In your linear algebra course, you will see that if $z$ is a complex number, its conjugate is represented by $z^*$, and if $A$ is a matrix, then its adjoint or transpose is represented by $A^{\rm T}$.

In C++, there are two arithmetic operators that are unary: + and -, and both must prefix its single operand. For example, -3 represents the integer 3 which is negated, while the + in +3 leaves the result unchanged. Of course, (- -3) and -(-3) result in the value 3.

Non-trivial cases where unary operators are used include the following:

	std::cout << -(1 + 2 + 3);
	std::cout << std::endl;

	std::cout << -(2*3*4);
	std::cout << std::endl;

	std::cout << -(1 - 2*3);
	std::cout << std::endl;

The unary + operator essentially makes no changes:

	std::cout << +(1 + 2 + 3);
	std::cout << std::endl;

	std::cout << +(2*3*4);
	std::cout << std::endl;

	std::cout << +(1 - 2*3);
	std::cout << std::endl;

Now, take a look at the following:

#include <iostream>

int main();

int main() {
	std::cout << (4 + - + - - 5 * - + 6 - + - 8)
	std::cout << std::endl;

	return 0;
}

Here, the only way we can make sense of this is to interpret the first operator as binary, and anything else must be unary, so this expression must mean $4 + (-(+(-(-5)))) \times (-(+6)) - (+(-8))$, which is the same as $4 + (-5) \times (-6) - (-8)$, which evaluates to $4 + 30 + 8 = 42$.

Now, almost all the time, we will never have expressions this complex, and it would be professional suicide to use it, what is interesting is that C++ will happily interpret it.

Examples of these examples of unary arithmetic operators can be found on repl.it.


Previous lesson Next lesson