C/C++ Arena

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

#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 task

This pull request adds stats for the match history page. The specification:

There are four bugs.

Challenge 2

This pull request adds three small math helpers, each with an integer bug. The specification: midpoint(a, b) is the average rounded toward a's side like a + (b - a) / 2, for any two ints without overflow; is_odd(x) works for negative numbers; change_percent(before, after) is (after - before) * 100 / before computed without overflow, and 0 when before is 0.

Challenge 3

This pull request has two bugs with unsigned sizes. sum_backwards never stops, because a std::size_t counter is always >= 0. fits can say yes to an enormous request, because used + needed wraps around past the largest std::size_t. Fix both.

Previous: Review: modifying while iterating Next: Review: class design