Introduction to Computer Programming | Source: Comprehensive Guide to For Loops in C++
Difficulty: Intermediate | Prerequisites: For loop syntax and structure (see Part 1 study notes)
Tags: for loop errors, infinite loop, off-by-one, OBOE, floating-point counter, semicolon error, loop best practices, numerical integration, Riemann sum, Monte Carlo, estimating Pi, C++ for loop
For loops are easy to get subtly wrong. This set of notes covers the three most common syntax mistakes (commas, stray semicolons, floating-point counters), off-by-one boundary errors, and good habits that keep loop code readable. It then walks through two applied uses of for loops: numerical integration (summing rectangles under a curve) and estimating Pi with both integration and Monte Carlo methods.
Empty loop (accidental semicolon loop)
A for loop whose header is immediately followed by a semicolon, making the body an empty statement. The intended body block then executes only once, outside the loop.
In simple terms, a rogue ; after the closing parenthesis turns the loop into a no-op.
Floating-point counter
Using a double or float as the loop control variable. Because floating-point arithmetic introduces tiny rounding errors, the termination condition may not trigger at the expected value.
Think of it as a tape measure that stretches slightly with every reading: after enough steps the numbers no longer line up.
Numerical integration (Riemann sum)
Approximating the area under a curve by dividing the region into many thin rectangles, computing each rectangle's area, and summing them. The more rectangles you use, the closer the sum gets to the true integral.
In simple terms, chop the curve into tiny slices, measure each slice, and add them up.
Monte Carlo method
A technique that uses random sampling to estimate a numerical result. For estimating Pi, random points are scattered inside a square and the proportion that land inside an inscribed circle gives the estimate.
Think of it as throwing darts at a dartboard and counting how many land inside the circle versus the square frame.
Three mistakes come up repeatedly in introductory courses.
Using commas instead of semicolons
// Wrong
for (int i = 0, i < 10, i++) { ... }
// Right
for (int i = 0; i < 10; i++) { ... }The three parts of the header must be separated by semicolons. Commas have a different meaning in C++ (the comma operator), and the compiler will either reject the code or produce unexpected behaviour.
Placing a semicolon after the closing parenthesis
for (int i = 0; i < 10; i++); // empty loop
{
cout << i << endl; // runs once, and i is out of scope
}The semicolon becomes the loop body (an empty statement). The loop spins through all its iterations doing nothing, and the braced block afterwards executes exactly once. This is difficult to spot because the code compiles without warnings on most compilers.
Using a floating-point variable as the counter
double a;
for (a = 0; a < 500; a += 0.001) {
// ...
}Floating-point addition accumulates tiny rounding errors. After hundreds of thousands of additions, a may not equal the value you expect, and the loop may run one iteration too many or too few. Use an integer counter and compute the floating-point value inside the body instead:
for (int i = 0; i < 500000; i++) {
double a = i * 0.001;
// ...
}An off-by-one error (OBOE) happens when the boundary condition is slightly wrong, causing the loop to execute one iteration too many or too few.
Factorial example:
// Wrong: uses < instead of <=
for (int i = 2; i < n; i++) {
nFactorial *= i;
}This runs n - 2 times instead of n - 1. The multiplication by n itself is skipped, so the result is (n-1)! rather than n!.
// Correct
for (int i = 2; i <= n; i++) {
nFactorial *= i;
}How to catch OBOEs:
Test with small values by hand. For a factorial loop, trace through n = 3 or n = 4 on paper.
Check the first iteration: does i start at the right value?
Check the last iteration: does i reach the value it should?
Count the total iterations and compare with what the problem requires.
Do not modify the loop control variable inside the body. Let the update expression in the header handle all changes to the counter. Changing it manually inside the body creates hard-to-find bugs and makes the loop's iteration count unpredictable.
Keep the header simple. The initialisation, condition, and update should each do one clear thing. If you need additional variable changes, place them inside the body or before the loop, not crammed into the header.
Add blank lines around loops. A blank line before and after a for loop (or any control structure) improves readability and makes the loop's boundaries visually obvious.
Limit nesting depth. Try to keep loops nested no more than three levels deep. Beyond that, the code becomes difficult to follow and error-prone. If you find yourself going deeper, consider extracting the inner logic into a separate function.
For loops are a natural fit for numerical integration because the process is inherently repetitive: divide, compute, accumulate.
The idea (Riemann sum):
Split the interval [a, b] into N equal sub-intervals, each of width dx = (b - a) / N.
For each sub-interval, compute the function's value at one point (often the right endpoint).
Multiply that height by dx to get the rectangle's area.
Sum all the rectangles.
Worked example: estimating the area under y = sqrt(1 - x^2) from 0 to 1 (a quarter circle of radius 1).
int N = 1000;
double totalArea = 0.0;
double dx = 1.0 / N;
for (int i = 1; i <= N; i++) {
double xi = i * dx;
double height = sqrt(1 - xi * xi);
double area = height * dx;
totalArea += area;
}The true area of a quarter circle with radius 1 is Pi / 4, so totalArea approximates Pi / 4. Multiplying by 4 gives an estimate of Pi. As N increases, the approximation improves because the rectangles become thinner and fit the curve more closely.
Real-world connection: this is the same principle engineers use in tools like MATLAB or spreadsheets to approximate integrals that have no closed-form solution. Any time you hear "numerical methods," a loop summing small pieces is usually at the core.
Two approaches, both driven by for loops.
Approach 1: numerical integration
The area of a full circle of radius 1 is Pi. A quarter of that circle sits above the x-axis from 0 to 1, described by y = sqrt(1 - x^2). Integrating 4 * sqrt(1 - x^2) from 0 to 1 gives Pi. The Riemann sum loop from the previous section does exactly this when you multiply totalArea by 4.
Approach 2: Monte Carlo
Generate a large number of random (x, y) points where both x and y lie in [0, 1]. For each point, test whether x^2 + y^2 <= 1 (i.e. the point falls inside the quarter circle). The ratio of points inside the circle to total points approximates Pi / 4.
Pi is approximately 4 * (points inside circle) / (total points).
A for loop controls the number of random trials.
Comparison of the two methods:
Aspect | Numerical integration | Monte Carlo |
|---|---|---|
Type | Deterministic | Stochastic (random) |
Convergence | Faster, predictable | Slower, depends on sample size |
Flexibility | Tied to functions you can evaluate | Works for complex or irregular shapes |
Implementation | Sum of function values in a loop | Random point generation in a loop |
Both are excellent demonstrations of how a simple counted loop can solve problems that look far more complex than a loop might suggest.
Students often think a semicolon after the for header is harmless. It is not. It creates an empty loop, and the intended body runs only once.
Students often assume floating-point counters will terminate at exactly the right value. They will not. Accumulated rounding errors can cause the loop to overshoot or undershoot the boundary.
Students sometimes believe that increasing N in a Riemann sum always gives a "perfect" answer. It improves accuracy, but floating-point precision sets a practical ceiling: beyond a certain N, rounding errors in the summation start to dominate.
Students frequently think Monte Carlo is unreliable because it uses randomness. It is not unreliable, just slower to converge. With enough samples it produces a good estimate, and its strength is handling shapes where integration formulae are unavailable.
Expect a "find the bug" question featuring one of the three common syntax errors. Be able to explain what the code does wrong and how to fix it.
Off-by-one errors are a perennial exam topic, often embedded in a factorial or summation problem.
The numerical integration example is a likely exam question in courses that cover applied programming: know the structure (init totalArea, loop with dx, accumulate) and be able to modify it for a different function.
You may be asked to compare numerical integration and Monte Carlo. The key distinction: deterministic versus stochastic, and their convergence behaviour.
True or false: for (int i = 0; i < 10; i++); prints nothing even if a cout statement follows in braces. (True.)
Fill in the blank: Using a double as a loop counter can cause ______ errors due to rounding. (floating-point / precision)
True or false: In a Riemann sum, making N larger always improves the estimate without limit. (False, floating-point precision eventually caps improvement.)
Fill in the blank: In the Monte Carlo method for Pi, the estimate formula is Pi approximately equals 4 times ______ divided by total points. (points inside the circle)
True or false: Modifying the loop control variable inside the body is considered good practice. (False.)
Q: Identify and fix the error: for (int i = 0, i < 5, i++) { cout << i; }
A: The commas should be semicolons. Corrected: for (int i = 0; i < 5; i++) { cout << i; }
Q: What does this code print? for (int i = 0; i < 3; i++); { cout << "hello"; }
A: It prints hello once. The semicolon after the parentheses creates an empty loop that runs three times doing nothing. The braced block then executes once.
Q: Why is for (double x = 0; x < 1.0; x += 0.1) risky?
A: Floating-point rounding means x may not hit exactly 1.0 after 10 additions of 0.1. The loop might run 9 or 11 times instead of the expected 10. Use an integer counter and compute x = i * 0.1 inside the body.
Q: Write a Riemann sum loop to approximate the integral of f(x) = x^2 from 0 to 1 using N = 100 rectangles.
A: double total = 0.0; double dx = 1.0 / 100; for (int i = 1; i <= 100; i++) { double xi = i * dx; total += xi * xi * dx; } The result approximates 1/3.
Q: In the Monte Carlo method for Pi, why do you multiply the ratio by 4?
A: The random points cover a quarter of the unit circle (the portion in the first quadrant). The ratio of hits to total points approximates Pi / 4, so multiplying by 4 recovers the full value of Pi.
Off-by-one errors reappear whenever you work with arrays, where accessing index n in an array of size n is undefined behaviour. The numerical integration technique extends to Simpson's rule and trapezoidal rule, which you will meet in later maths or numerical methods courses. Monte Carlo methods show up across scientific computing, from physics simulations to financial modelling, wherever exact integration is impractical.
for loop errors C++, infinite loop C++, off-by-one error, OBOE, boundary condition bug, semicolon after for loop, floating-point loop counter, empty loop, loop best practices, nesting depth, Riemann sum C++, numerical integration for loop, area under curve, estimating Pi C++, Monte Carlo method C++, random sampling, deterministic vs stochastic, rectangle method, quarter circle area