Search

3.2 — Arithmetic operators

Unary arithmetic operators

There are two unary arithmetic operators, plus (+), and minus (-). If you remember, unary operators are operators that only take one operand.

Operator Symbol Form Operation
Unary plus + +x Value of x
Unary minus - -x Negation of x

The unary plus operator returns the value of the operand. In other words, +5 = 5, and +x = x. Generally you won’t need to use this operator since it’s redundant. It was added largely to provide symmetry with the unary minus operator.

The unary minus operator returns the operand multiplied by -1. In other words, if x = 5, -x = -5.

For best effect, both of these operators should be placed immediately preceding the operand (eg. -x, not - x).

Do not confuse the unary minus operator with the binary subtraction operator, which uses the same symbol. For example, in the expression x = 5 - -3;, the first minus is the subtraction operator, and the second is the unary minus operator.

Binary arithmetic operators

There are 5 binary arithmetic operators. Binary operators are operators that take a left and right operand.

Operator Symbol Form Operation
Addition + x + y x plus y
Subtraction - x - y x minus y
Multiplication * x * y x multiplied by y
Division / x / y x divided by y
Modulus (Remainder) % x % y The remainder of x divided by y

The addition, subtraction, and multiplication operators work just like they do in real life, with no caveats.

Division and modulus (remainder) need some additional explanation.

Integer and floating point division

It is easiest to think of the division operator as having two different “modes”. If both of the operands are integers, the division operator performs integer division. Integer division drops any fractions and returns an integer value. For example, 7 / 4 = 1 because the fraction is dropped. Note that integer division does not round.

If either or both of the operands are floating point values, the division operator performs floating point division. Floating point division returns a floating point value, and the fraction is kept. For example, 7.0 / 3 = 2.333, 7 / 3.0 = 2.333, and 7.0 / 3.0 = 2.333.

Note that trying to divide by 0 (or 0.0) will generally cause your program to crash, as the results are undefined!

Using static_cast<> to do floating point division with integers

In section 2.7 -- Chars, we showed how we could use the static_cast<> operator to cast a char to an integer so it would print correctly.

We can similarly use static_cast<> to convert an integer to a floating point number so that we can do floating point division instead of integer division. Consider the following code:

This produces the result:

int / int = 1
double / int = 1.75
int / double = 1.75
double / double = 1.75

Modulus (remainder)

The modulus operator is also informally known as the remainder operator. The modulus operator only works on integer operands, and it returns the remainder after doing integer division. For example, 7 / 4 = 1 remainder 3, thus 7 % 4 = 3. As another example, 25 / 7 = 3 remainder 4, thus 25 % 7 = 4.

Modulus is very useful for testing whether a number is evenly divisible by another number: if x % y == 0, then we know that y divides evenly into x.

For example, say we wanted to write a program that printed every number from 1 to 100 with 20 numbers per line. We could use the modulus operator to determine where to do the line breaks. Even though you haven’t seen a while statement yet, the following program should be pretty comprehensible:

This results in:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

We’ll discuss while statements in a future lesson.

A warning about integer division and modulus with negative numbers prior to C++11

Prior to C++11, if either of the operands of integer division are negative, the compiler is free to round up or down! For example, -5 / 2 can evaluate to either -3 or -2, depending on which way the compiler rounds. However, most modern compilers truncate towards 0 (so -5 / 2 would equal -2). The C++11 specification changed this to explicitly define that integer division should always truncate towards 0 (or put more simply, the fractional component is dropped).

Also prior to C++11, if either operand of the modulus operator is negative, the results of the modulus can be either negative or positive! For example, -5 % 2 can evaluate to either 1 or -1. The C++11 specification tightens this up so that a % b always resolves to the sign of a.

Arithmetic assignment operators

Operator Symbol Form Operation
Assignment = x = y Assign value y to x
Addition assignment += x += y Add y to x
Subtraction assignment -= x -= y Subtract y from x
Multiplication assignment *= x *= y Multiply x by y
Division assignment /= x /= y Divide x by y
Modulus assignment %= x %= y Put the remainder of x / y in x

Up to this point, when you’ve needed to add 5 to a variable, you’ve likely done the following:

This works, but it’s a little clunky, and takes two operators to execute.

Because writing statements such as x = x + 5 is so common, C++ provides 5 arithmetic assignment operators for convenience. Instead of writing x = x + 5, you can write x += 5. Instead of x = x * y, you can write x *= y.

Where’s the exponent operator?

You’ll note that the ^ operator (commonly used to denote exponentiation in standard mathematical nomenclature) is a Bitwise XOR operation in C++. C++ does not include an exponent operator. To do exponents in C++, #include the <cmath> header, and use the pow() function:

Note that the parameters (and return value) of pow in the above example are of type double. std::pow() assumes the base is a floating point number. Note that due to rounding errors in floating point numbers, the results of pow() may not be precise (slightly smaller or larger than what you’d expect).

If you want to do integer base, you’re best off just using your own function to do so, like this one (that uses the non-intuitive “exponentiation by squaring” algorithm for efficiency):

Don’t worry if you don’t understand all of the parts of this function yet. Just beware of overflowing your integer result, which can happen very quickly if either argument is large.

Quiz
1) What does the following expression evaluate to? 6 + 5 * 4 % 3

2) Write a program that asks the user to input an integer, and tells the user whether the number is even or odd. Write a function called isEven() that returns true if an integer passed to it is even. Use the modulus operator to test whether the integer parameter is even.

Hint: You’ll need to use if statements and the comparison operator (==) for this program. See lesson 2.6 -- Boolean values and an introduction to if statements if you need a refresher on how to do this.

Answers

1) Show Solution

2) Show Solution

3.3 -- Increment/decrement operators, and side effects
Index
3.1 -- Operator precedence and associativity

212 comments to 3.2 — Arithmetic operators

  • David

    Hi Alex

    Firstly, huge thanks for this tutorial series. You have a great teaching style, and I really like how you take the time to talk about good programming practices and pitfalls.

    Regarding the following code:

    I was playing around with debug->disassembly in VS2017 and noticed that this actually compiles to more instructions in assembly than the if/else version, despite it seeming shorter/simpler in C++.

    I didn't check the cycles per instruction, but either way, I thought this tutorial might be a good opportunity for you to bring up the topic of premature optimisation versus code readability (especially as I understand modern compilers can optimise better than most humans, and due to modern CPU features like speculative execution).

    I'm just a novice/hobbyist programmer, so I used to be a bit OCD about trying to write efficient code (or at least code that I thought was efficient), including avoiding function calls due to their overhead, but am trying now to re-train myself to prioritise code readability. Personally I think the if/else version is just a tiny bit easier to understand intuitively at a glance.

    Thanks again for your work on these tutorials!

    • Hi David!

      Assembly has a very limited instruction set, whereas C++ is huge. You'll get a lot more instructions out of your code, no matter what you write.
      Low-level optimization should be left to the compiler in most cases. The programmer should prioritize optimization of algorithms, data usage, etc., because those can't be optimized by the compiler and they have a much bigger impact than a couple of unnecessary instructions.

      • David

        Yep! I got a little bit of exposure to ARM assembly a while back due to trying to mod some GBA/NDS games. It was fun and I learned a lot, but man is it good to be back in a higher level language and not have to keep track of things like the stack pointer for local variables!!

        Agree with your points! I didn't really appreciate that when I started programming though, which is why I thought it might be a good topic for Alex to address in these tutorials as part of good programming practices. Before I was exposed to assembly, I used to think that less C++ code meant less instructions to execute - now I know that is not necessarily the case (for the isEven() example, both versions actually compile into the same assembly for Release build - just 3 instructions)! Now I also know that less instructions does not necessarily equal better performance (or matter nearly as much as algorithms and data usage, as you say).

        Hence when it comes to choosing between if/else blocks versus code that looks shorter in C++, I now try to prioritise readability. It might be different for seasoned programmers like yourself, but I have to mentally convert that single line return statement in my head into an if/else statement when reading it anyway, so the if/else version just reads a tiny bit easier for me. The following code from Chapter 3.5 puts this in starker contrast. Also harder to debug I think? Breaking it out into if/else statements made it easier to follow in the debugger for me.

        • > Also harder to debug
          Generally, if you use more lines, it's easier to debug. Using more lines doesn't necessarily change the compiled code.
          @approximatelyEqual can be expanded into multiple lines without affecting the outcome. The one-liners might be easier written if you've been coding for a while. But once something goes wrong you'll be back to if/else and temporary variables.

          You can edit your posts. The syntax highlighter will work after refreshing the page.

          • David

            > You can edit your posts. The syntax highlighter will work after refreshing the page.
            Ha thanks! That's exactly why I deleted/re-posted. Hope you didn't have to retype your message because of that!

        • Alex

          Great conversational points, and the reminder not to prematurely optimize your code is timely as I'm rewriting a lesson where a mention of that would be perfect.

          I agree the approximatelyEqual() function is hard to read/follow and even harder to debug -- but I opted to prefer the terse version under the assumption that most readers would just use the function, not try to dissect it. 🙂 Maybe that's the wrong call to make in a learning tutorial series...

          • David

            > I'm rewriting a lesson where a mention of that would be perfect

            Awesome! Is there a way to get email alerts when one of the lessons I've already worked through gets updated (and maybe also see what changed)? Couldn't find a subscribe button anywhere.

            > I opted to prefer the terse version under the assumption that most readers would just use the function

            Haha I think maybe you also chose it because you knew if you used the long version, you'd have to put up with lots of clever people telling you "did you know you could do that all in one line?" :p I could follow the logic in my head, but needed the debugger to figure out what was happening to the adjusted epsilon for the close-to-zero case. (Who knew multiplying a number by something <1 made that number smaller haha - my maths is sooo rusty!)

            On the topic of debugging, I remember a few years ago when I first worked through these lessons (got up to Chapter 14) that the debugger was barely mentioned again after introducing it in Chapter 1 (from a quick site search it seems this is still the case). So back then I never really used the debugger - mostly due to lack of exposure and because I was used to just inserting "debug code" (eg, tracking how variables changed by printing to console), which is a habit I developed from modding games using LUA scripting. But after trying to make sense of ARM disassembly... my God how I appreciate a good debugger now!

            As you update lessons for other issues, perhaps it would be helpful to start sprinkling in more exercises using the debugger (just to get new programmers more used to it). For example, in this lesson, the int pow() "exponentiation by squaring" algorithm causes an infinite loop if a negative exponent is entered. You could mention this and encourage students to use the debugger to figure out why (apparently depends on the compiler, but VS17 uses arithmetic shift right for signed int so the loop gets stuck at -1).

            (I know you encourage robust code in later chapters, but I used to be super impatient and hate thinking about edge cases or investing the time/lines required. Until I started writing code to parse documents, and kept creating documents that broke my own code or which accidentally created infinite loops. Sometimes ya just gotta learn the hard way...)

  • Rai

    So I gave myself a challenge to write a geometric sequence and then display the last number in the sequence and state whether it's even or odd.

    1. Is there any way I can improve this

    2. I'm beginning to understand while loops. How do I do this:
    if the user inputs a decimal number for "Enter a power: ". It'll output an speech and loop them back to question "Enter a power: " again until they give an integer without decimal point.

    • Hi Rai!

      1.
      * Initialize your variables with uniform initialization
      * Line 32: You can use single quotation marks to make your numbers more readable (1'000'000)
      * Line 32: This loop could be infinite if @power <= 1
      * Use double numbers when calculating with doubles (1.0 instead of 1 etc.)

      2.

      • Rai

        Thanks. One thing. You told me about Initialize your variables with uniform initialization before but I don't understand what this means - how to use {} to initialize variables. Could you show an example.

      • This is part of lesson 2.1. Unfortunately, Alex doesn't use uniform initialization himself in most of the lessons.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]