Step 5 of 6
Review: integer math
Numeric code gets reviewed for four things: overflow, division by zero, signed/unsigned mix-ups, and empty input. None of them shows up with small, friendly test values, which is exactly why reviewers try unfriendly ones.
The four checks
- Overflow: can an intermediate result exceed the type's range? An
intholds about ±2.1 billion.a * 100overflows onceais just over 21 million. Signed overflow is undefined behavior, not just a wrong number. Do the math in a wider type (long long) before the operation, not after. - Division by zero: is the divisor ever 0? For
intit's undefined behavior (often a crash); fordoubleit givesinfornan. - Unsigned wraparound:
size_tcan't be negative.v.size() - nwithn > v.size()wraps to a gigantic number. - Empty input: what does the function return for no data? The spec usually says.
#include <cstdint>
#include <iostream>
#include <vector>
// Percentage of bytes used, rounded down; 0 for a zero-size disk.
int percent_used(std::int64_t used, std::int64_t total) {
if (total == 0) return 0; // division by zero
return static_cast<int>(used * 100 / total); // 64-bit math: no overflow
}
// Sum of the first n readings (all of them if fewer).
long long first_n(const std::vector<int>& r, std::size_t n) {
long long sum = 0; // wide accumulator
for (std::size_t i = 0; i < n && i < r.size(); i++) sum += r[i];
return sum;
}
int main() {
std::cout << percent_used(3'000'000'000LL, 4'000'000'000LL) << "\n";
std::cout << percent_used(0, 0) << "\n";
std::cout << first_n({2'000'000'000, 2'000'000'000}, 5) << "\n";
std::cout << first_n({}, 3) << "\n";
}
75
0
4000000000
0
Every line handles one of the four checks: the zero guard, 64-bit multiplication, a long long sum that can hold two billion plus two billion, and a loop bound that respects both n and the real size.
Where the conversion goes
static_cast<long long>(a) * 100 widens before multiplying. static_cast<long long>(a * 100) multiplies in int first, overflows, and only then widens the already wrong value. The same applies to sums: the accumulator's type must be wide from the start.
Your turn: this pull request adds stats for the match history page. The specification:
win_rate(wins, games): whole-number percentage, rounded down; 0 when there are no games. It must work for career totals in the billions.average(scores): the mean as a double; 0.0 for no scores. Scores can be large.last_n_total(scores, n): the sum of the lastnscores (all of them if there are fewer than n).
There are four bugs.
Previous: Review: modifying while iterating Next: Review: class design