3. Numbers & Math¶
What & why¶
Numbers in C++ map directly onto the hardware. That makes them fast — and it means you need to know the hardware's rules: integers have fixed sizes and can overflow, integer division throws away the remainder, and floating-point numbers are approximations.
Arithmetic operators¶
int a = 17, b = 5;
a + b // 22
a - b // 12
a * b // 85
a / b // 3 ← integer division: the fraction is discarded
a % b // 2 ← remainder (modulo), integers only
Integer division vs. floating-point division¶
The type of the operands decides which division happens:
7 / 2 // 3 (int / int)
7.0 / 2 // 3.5 (double / int → double)
7 / 2.0 // 3.5
static_cast<double>(7) / 2 // 3.5
A classic bug:
int total = 7, count = 2;
double avg = total / count; // 3.0 — division happened in int!
double avg2 = static_cast<double>(total) / count; // 3.5 — correct
Integer division truncates toward zero: -7 / 2 is -3, and -7 % 2 is -1.
Compound assignment and increment¶
int x = 10;
x += 5; // x = x + 5 → 15
x -= 3; // 12
x *= 2; // 24
x /= 4; // 6
x %= 4; // 2
++x; // pre-increment: add 1, then use the value
x++; // post-increment: use the value, then add 1
--x;
Prefer ++x when you don't need the old value. Never modify the same variable twice in one expression (x = x++ + ++x;) — the result is undefined or unspecified.
Precedence¶
*, /, % bind tighter than +, -. Use parentheses whenever it helps a reader:
Overflow¶
Integers have limits:
#include <iostream>
#include <limits>
int main() {
std::cout << std::numeric_limits<int>::max() << '\n'; // 2147483647
std::cout << std::numeric_limits<int>::min() << '\n'; // -2147483648
}
- Signed overflow (
int) is undefined behavior. The compiler assumes it never happens; anything can result. - Unsigned overflow wraps around:
0u - 1is4294967295.
Use long long (or std::int64_t) when values can exceed ~2 billion.
Floating-point pitfalls¶
#include <cmath>
#include <iostream>
int main() {
double x = 0.1 + 0.2;
std::cout << (x == 0.3) << '\n'; // 0 (false!)
std::cout << std::abs(x - 0.3) < 1e-9 ? "close" : "far";
}
Oops — that last line doesn't even compile: << binds tighter than <, so it tries to compare the stream itself with 1e-9. Parenthesize:
Rules:
- Never compare floating-point values with
==. Compare with a tolerance. - Use
double, notfloat, unless you have a specific reason. - Never use floating point for money. Store cents in an integer.
Controlling printed precision:
<cmath>¶
#include <cmath>
std::sqrt(16.0) // 4
std::pow(2.0, 10) // 1024
std::abs(-5) // 5
std::floor(2.7) // 2
std::ceil(2.1) // 3
std::round(2.5) // 3
std::fmod(7.5, 2) // 1.5 (remainder for doubles)
std::hypot(3.0, 4.0) // 5
#include <numbers>
std::numbers::pi // 3.14159... (C++20)
Random numbers with <random>¶
Forget rand(). Use an engine plus a distribution:
#include <iostream>
#include <random>
int main() {
std::random_device rd; // seed source
std::mt19937 gen{rd()}; // Mersenne Twister engine
std::uniform_int_distribution<int> die{1, 6}; // inclusive range
for (int i = 0; i < 5; ++i) {
std::cout << die(gen) << ' ';
}
std::cout << '\n';
std::uniform_real_distribution<double> unit{0.0, 1.0};
std::cout << unit(gen) << '\n';
}
Create the engine once and reuse it. Seeding a new engine every call gives poor randomness.
Number literals¶
1'000'000 // digit separators for readability
0xFF // hexadecimal (255)
0b1010 // binary (10)
3.0e8 // scientific notation
42u 42L 42LL // unsigned, long, long long
2.5f // float
Common mistakes¶
double avg = sum / count;with twoints — integer division. Cast first.- Comparing doubles with
==. %on doubles — doesn't compile. Usestd::fmod.- Dividing by zero — integer division by zero is undefined behavior (usually a crash).
std::powfor integers — it returns adouble;std::pow(10, 2)might be99.9999...when truncated. Multiply integers directly.absvsstd::abs— the unqualified Cabsonly handlesint. Usestd::abswith<cmath>.
Exercises¶
- Read two integers and print their sum, difference, product, integer quotient, remainder, and exact (floating-point) quotient.
- Read a total number of seconds and print
H:MM:SS(use/and%). - Compute the hypotenuse of a right triangle from two side lengths.
- Simulate rolling two dice 10,000 times and print how often each total (2–12) appears.
- Print
std::numeric_limits<int>::max(), then add 1 to it and print again. Compile with-fsanitize=undefinedand run. What happens? - Compound interest: read a principal, annual rate, and years, and print the final amount to 2 decimal places.
Recap¶
int / intis integer division; cast todoublefor a real quotient.%gives the remainder (integers only).- Signed overflow is undefined behavior; use wider types when needed.
- Floating point is approximate: compare with a tolerance, never use it for money.
<cmath>for math functions,<numbers>for constants,<random>for randomness.
Next: Strings →