Arithmetic Operators in C++
Two settings trigger conversions: arithmetic operations, where values are combined with an operator, and assignments, where a value is stored into a variable. Both the operands and the receiving variable therefore matter.
A Cost Calculation with Two Types
Suppose you are writing a small program that calculates the cost of some items. The number of items is a whole number, while the price of each item may include cents. In a real program these two values often live in variables of different basic types, and you want to combine them in one arithmetic expression. The question for this article is simple: when you use an arithmetic operator on values of different types, what does C++ do?
What do you think happens?
Suppose items holds the whole number 3 and price holds the fractional value 2.25. What does the expression items * price produce?
Reveal answer
6.75
The int value 3 is brought into agreement with the double value 2.25, the multiplication is carried out on doubles, and the result 6.75 keeps the fractional part.
Why Mixed Types Can Work at All
Arithmetic operators such as addition, subtraction, multiplication, and division are not attached to one single type. They work with the basic types, and they work with combinations of those types where the combination makes sense for the calculation. The word appropriate matters: the operator is not a blanket permission for every pairing of values.
The reason a mixed expression can work at all is that C++ performs all meaningful conversions between the basic types so that they can be mixed freely. The language is willing to bring values of different basic types into agreement so the operation can proceed. Mixing types is therefore a supported language feature. The key question is always whether the combination is meaningful for the calculation you are doing.
Two settings trigger conversions: arithmetic operations, where values are combined with an operator, and assignments, where a value is stored into a variable. Both the operands and the receiving variable therefore matter.
Tracing a Mixed Expression Step by Step
Return to the opening calculation. One variable holds a whole number of items, and another holds a price with a fractional part. The program below multiplies them and stores the result in a third variable. Read the code first, then compare it with the prediction you made earlier.
#include <iostream>
int main() {
int items = 3;
double price = 2.25;
double total = items * price;
std::cout << total << std::endl;
return 0;
}- 1Line 4 creates the int variable items with the value 3.
- 2Line 5 creates the double variable price with the value 2.25.
- 3Line 6 evaluates items * price. Because one operand is a double, the int 3 is converted so the multiplication can proceed on doubles.
- 4The multiplication produces 6.75, and that double value is assigned to total.
- 5Line 7 prints total, which shows 6.75.
- items is an int holding the whole number 3
- price is a double holding the fractional value 2.25
- items * price mixes an int operand with a double operand
- total is a double, so the stored result keeps its fractional part
6.75Notice that the example involves two steps where conversion matters: the operation itself and the assignment that stores its result. Both situations are covered by the same idea of free mixing. The conversion of the int operand happens so the multiplication has a common type to work with, and the assignment then stores the double result into total without changing it.
Conversion During Assignment
Assignment is the second setting where conversions happen. When a value of one basic type is assigned to a variable of another basic type, C++ performs a conversion so the assignment can happen. The idea is that conversion puts a value into a form the destination variable can hold.
#include <iostream>
int main() {
int whole = 7.9;
std::cout << whole << std::endl;
return 0;
}- 1Line 4 assigns the double value 7.9 to the int variable whole.
- 2Because whole is an int, the fractional part is dropped and the stored value is 7.
- 3Line 5 prints 7, not 7.9.
- The initializer 7.9 is a double
- whole is an int, so it cannot hold a fractional part
- The stored value is 7
7The stored value is the destination's representation of the source, not the original number. An int has no place for a fractional part, so the value that arrives in whole is 7. Keep this in mind whenever a double result flows into an int variable.
Integer and Floating-Point Division
The operand types decide how a division is carried out. When both operands are int, C++ performs integer division and the fractional part of the quotient is discarded. When one operand is a double, the division is carried out on doubles and the fractional part is kept. The same symbol, /, therefore produces different answers depending on the types of its operands.
#include <iostream>
int main() {
std::cout << 7 / 2 << std::endl;
std::cout << 7 / 2.0 << std::endl;
return 0;
}- 1Line 4 divides two int literals, so the result is the int 3.
- 2Line 5 divides the int 7 by the double 2.0. The int is converted to a double, and the result is 3.5.
- 7 / 2 has two int operands
- 7 / 2.0 has one double operand
- The literal 2.0 is a double, which changes the kind of division
3
3.5Operators That Need Specific Types
Because the word appropriate matters, not every operator accepts every pairing. The remainder operator % is defined for integer operands. Asking it to work on a double is a combination that does not make sense for that operator, so the program does not compile.
Mistakes Beginners Make with Mixed Types
Common mistake Assuming 7 / 2 gives 3.5 because the program is doing arithmetic on decimal values
What it looks likestd::cout << 7 / 2 << std::endl; prints 3Why it failsBoth operands are int, so C++ performs integer division and discards the fractional part.
Do this insteadUse a double operand, such as 7 / 2.0, when the fractional result is needed.
Common mistake Assuming a double result stored in an int keeps its fractional part
What it looks likeint whole = 7.9; leaves whole holding 7Why it failsAn int cannot hold a fractional part, so the conversion during assignment drops it.
Do this insteadStore the result in a double if the fractional part must be kept, or decide explicitly how the value should be reduced.
Common mistake Assuming every operator accepts every type combination the same way
What it looks likeUsing % with a double operand, as in 7.5 % 2
Why it failsThe remainder operator is defined for integer operands, so this combination does not compile.
Do this insteadCheck that the operator is defined for the operand types before writing the expression.
Common mistake Predicting results from the look of the numbers instead of from the types of the operands
Practice Predicting Mixed-Type Results
Before running any code, predict the output of each line: (1) std::cout << 9 / 4 << std::endl; (2) std::cout << 9 / 4.0 << std::endl; (3) int n = 4.6; std::cout << n << std::endl;. For each one, name the types of the operands or the variable that receives the value, and say whether the fractional part survives.
Hints
- Look at whether both operands of the division are int or whether one is a double.
- For the assignment, ask what an int variable can store.
- Check the operand types first, then the result.
Key Takeaways
- C++ performs all meaningful conversions between the basic types so that they can be mixed freely in arithmetic and assignment.
- Arithmetic operators work with appropriate combinations of types, so the key question is whether the combination is meaningful for the calculation.
- When an int and a double meet in one expression, the int operand is converted so the operation runs on a common type, and the double result can then be stored.
- Assigning a double to an int converts the value, and the fractional part is dropped.
- Two divisions that look alike can differ: 7 / 2 gives 3 with int operands, while 7 / 2.0 gives 3.5 with a double operand.