Unary arithmetic operators

Unary operators work with a single operand. C++ provides two unary arithmetic operators:

Operator Symbol Form Operation
Unary plus + +value Returns the value unchanged
Unary negation - -value Returns the negated value

The unary negation operator flips the sign of a number. If you have int temperature{25}, then -temperature evaluates to -25.

The unary plus operator simply returns the value as-is. While rarely used (since it doesn't change anything), it exists mainly for symmetry with unary negation.

For clarity, place these operators directly before their operand with no space: -temperature, not - temperature.

Don't confuse these operators with their binary counterparts. In the expression int difference{50 - -25};, the first minus performs subtraction, while the second is unary negation.

Binary arithmetic operators

Binary operators require two operands (one on the left, one on the right):

Operator Symbol Form Operation
Addition + a + b Sum of a and b
Subtraction - a - b Difference between a and b
Multiplication * a * b Product of a and b
Division / a / b Quotient of a divided by b
Remainder % a % b Remainder after dividing a by b

Addition, subtraction, and multiplication work exactly as you'd expect from mathematics.

Division requires special attention depending on the types involved. The remainder operator will be covered in the next lesson.

Integer versus floating-point division

The division operator behaves differently depending on operand types:

Floating-point division occurs when at least one operand is a floating-point type. The result preserves fractional parts. For example, 15.0 / 4 equals 3.75, as does 15 / 4.0 and 15.0 / 4.0. Keep in mind that floating-point calculations can have rounding errors.

Integer division occurs when both operands are integers. The fractional part is discarded, leaving only the whole number. For example, 15 / 4 equals 3 (not 3.75). Similarly, -15 / 4 equals -3 (the fraction is simply dropped).

Using static_cast<> for floating-point division with integers

What if you want to divide two integers but preserve the decimal result?

In the Introduction to type conversion and static_cast lesson, we learned how static_cast<> converts values between types.

We can use static_cast<> to convert an integer to a floating-point type, forcing floating-point division:

#include <iostream>

int main()
{
    constexpr int distance{100};
    constexpr int time{30};

    std::cout << "int / int = " << distance / time << '\n';
    std::cout << "double / int = " << static_cast<double>(distance) / time << '\n';
    std::cout << "int / double = " << distance / static_cast<double>(time) << '\n';
    std::cout << "double / double = " << static_cast<double>(distance) / static_cast<double>(time) << '\n';

    return 0;
}

This outputs:

int / int = 3
double / int = 3.33333
int / double = 3.33333
double / double = 3.33333

As shown, converting either operand (or both) to a floating-point type triggers floating-point division instead of integer division.

Division by zero

Warning
Dividing an integer by `0` causes undefined behavior and will typically crash your program. Always validate divisors before performing division.

Dividing an integer by 0 causes undefined behavior. The mathematical operation is meaningless, so your program may crash or behave unpredictably:

#include <iostream>

int main()
{
    constexpr int cookies{24};
    std::cout << "You have " << cookies << " cookies. Enter how many friends will share them: ";
    int friends{};
    std::cin >> friends;

    std::cout << "Each friend gets " << cookies / friends << " cookies.\n";

    return 0;
}

If you run this and enter 0, the program will likely crash. Try it yourself—it won't harm your system.

Dividing by floating-point 0.0 is implementation-defined. On systems supporting IEEE754 (most modern systems), you'll get NaN (Not a Number) or Inf (Infinity). On other systems, the result is undefined.

Related content: We discuss NaN and Inf in the Floating point numbers lesson.

Test your system's behavior:

#include <iostream>

int main()
{
    constexpr int cookies{24};
    std::cout << "You have " << cookies << " cookies. Enter servings: ";

    double servings{};
    std::cin >> servings;

    std::cout << "Each serving contains " << cookies / servings << " cookies.\n";

    return 0;
}

Arithmetic assignment operators

These operators combine arithmetic with assignment:

Operator Symbol Form Operation
Addition assignment += a += b Add b to a
Subtraction assignment -= a -= b Subtract b from a
Multiplication assignment *= a *= b Multiply a by b
Division assignment /= a /= b Divide a by b
Remainder assignment %= a %= b Store remainder of a / b in a

Previously, you might have written:

score = score + 10; // add 10 to score

This works but requires two operators: addition and assignment.

Arithmetic assignment operators provide a more concise syntax. Instead of score = score + 10, write score += 10. Instead of health = health * multiplier, write health *= multiplier.

The improved version:

score += 10; // add 10 to score

Modifying versus non-modifying operators

A modifying operator changes the value of one of its operands. Most C++ operators are non-modifying—they compute and return a result without changing operands.

Two categories of built-in operators modify their left operand:

  • Assignment operators: =, +=, -=, *=, /=, %=, and bitwise assignment operators (<<=, >>=, &=, |=, ^=)
  • Increment and decrement operators: ++ and -- (covered in the Increment/decrement operators, and side effects lesson)

Don't confuse these with relational operators like ==, !=, <=, and >=, which compare values without modification (covered in the Relational operators and floating point comparisons lesson).

Advanced note: Overloaded operators can behave differently than built-in ones. For instance, the overloaded operator<< for output streams modifies the stream object even though the built-in version doesn't modify operands.

Summary

  • Unary arithmetic operators: + (unary plus, returns value unchanged) and - (unary negation, flips sign)
  • Binary arithmetic operators: + (addition), - (subtraction), * (multiplication), / (division), % (remainder)
  • Integer division: When both operands are integers, the fractional part is discarded (e.g., 15 / 4 equals 3)
  • Floating-point division: When at least one operand is floating-point, the result preserves decimal values (e.g., 15.0 / 4 equals 3.75)
  • static_cast for division: Use static_cast<double>(value) to convert an integer to floating-point and force floating-point division
  • Division by zero: Dividing integers by zero causes undefined behavior (typically a crash); dividing floating-point by zero is implementation-defined (often produces NaN or Inf)
  • Arithmetic assignment operators: Combine arithmetic and assignment (+=, -=, *=, /=, %=) for more concise code (e.g., score += 10 instead of score = score + 10)
  • Modifying operators: Assignment operators and increment/decrement operators modify their left operand; most other operators are non-modifying

Understanding the difference between integer and floating-point division is crucial for preventing unexpected results in calculations. Always consider whether you need integer or floating-point division, and use static_cast<> when you need to convert between them. Be especially careful to avoid division by zero, which can crash your program.