Avoiding Integer Overflow - C Plus Plus

  1. Which type is the narrowest listed type that is sufficient for the running total?
    Answer: std::int64_t, whose maximum exceeds the required total When choosing an integer type for a running total, you need to calculate the maximum possible value the total could reach, then find the narrowest type whose maximum is at least that large. Here, you have 5000050000 measurements, each at most 100000100000, so the worst-case total is 50000×100000=5,000,000,00050000 \times 100000 = 5{,}000{,}000{,}000. That's five billion — now check each type against that ceiling. std::int64_t (C) is the correct answer. Its maximum exceeds 9×10189 \times 10^{18}, which comfortably holds 5,000,000,0005{,}000{,}000{,}000. No overflow is possible. Choice A, std::int32_t, fails because its maximum is only 2,147,483,6472{,}147{,}483{,}647 — roughly 2.1 billion, which is less than the required 5 billion. Signed 32-bit overflow is undefined behavior in C++, making this dangerous. Choice B, std::uint32_t, has a maximum of 4,294,967,2954{,}294{,}967{,}295 — about 4.3 billion, still short of 5 billion. Even though it's unsigned (so overflow wraps rather than being undefined), it still produces an incorrect result. Many students assume 32-bit unsigned "should be enough," but the math says otherwise. Choice D, std::uint16_t, maxes out at 65,53565{,}535 — far too small even for a single measurement, let alone 50,000 of them. A useful strategy: always compute the worst-case product before picking a type, and don't guess by "feel." Powers of two don't always align neatly with real-world maximums, so arithmetic is the only reliable check. When 32-bit types fall just short, reach for 64-bit.
  2. Which statement safely calculates the result for every permitted value of n?
    Answer: std::int64_t pairs = static_cast<std::int64_t>(n) * (n - 1) / 2; Whenever you see arithmetic on large integers in C++, your first instinct should be: where does the multiplication happen, and in what type? Integer overflow occurs silently, so you must widen the type before the dangerous operation, not after. For n=100000n = 100000, the product n×(n−1)=100000×99999=9,999,900,000n \times (n-1) = 100000 \times 99999 = 9{,}999{,}900{,}000, which far exceeds the 32-bit signed maximum of roughly 2.1×1092.1 \times 10^9. Any multiplication that stays in int32_t will overflow, producing undefined behavior and a garbage result. Choice C, static_cast<std::int64_t>(n) * (n - 1) / 2, is correct because casting n to 64-bit before the multiplication forces the entire expression into 64-bit arithmetic. The compiler promotes (n - 1) to int64_t as well, so the product is computed safely, and the final result fits comfortably in int64_t. Choice A performs the entire multiplication in 32-bit (n * (n - 1) stays int32_t) and only stores the already-overflowed result into int64_t — too late. Choice B makes the same mistake: the cast wraps a parenthesized 32-bit multiplication, so overflow happens first, then the damaged value is widened. The cast must come before the multiply, not around it. Choice D uses the correct 64-bit arithmetic but stores the result in int32_t, which truncates the large value and still produces wrong output for big n. Study tip: In C++, widen at least one operand before the multiplication — not the result after. A cast applied to a complete subexpression arrives too late to prevent overflow.
  3. Which statement ensures that every multiplication is performed with sufficient range?
    Answer: std::uint64_t ms = static_cast<std::uint64_t>(days) * 24 * 60 * 60 * 1000; When converting days to milliseconds, you multiply by 24×60×60×1000=86,400,00024 \times 60 \times 60 \times 1000 = 86{,}400{,}000. For 100,000 days, the result is 8.64×10128.64 \times 10^{12}, which far exceeds a 32-bit unsigned integer's maximum of about 4.29×1094.29 \times 10^9. The critical question is: at which point in the chain of multiplications does the widening to 64 bits occur? C++ evaluates multiplication left-to-right, and the type of each intermediate result is determined by the operands at that step. If both operands are 32-bit, the multiplication is performed in 32-bit arithmetic — overflow can happen before the result is ever stored in a 64-bit variable. A is correct because casting days to std::uint64_t before any multiplication forces every subsequent multiplication in the chain to be performed in 64-bit arithmetic, since one operand is already 64-bit. No intermediate result can overflow. B is wrong because the multiplication chain days * 24 * 60 * 60 * 1000 is entirely in 32-bit arithmetic. The final result is then widened to 64-bit, but the damage — overflow — has already occurred. C is wrong for the same reason as B. The cast to std::uint64_t happens after the entire expression is evaluated in 32-bit, so it only widens an already-corrupted value. D is wrong because 1000ULL widens only the last multiplication (... * 60 * 1000). Earlier multiplications, like days * 24, still occur in 32-bit and can overflow before the suffix has any effect. Your takeaway: always cast before the first multiplication, not after. The placement of a cast or suffix determines which operations benefit from the wider type.
  4. Which statement prevents overflow in the intermediate calculation of the byte count?
    Answer: std::size_t bytes = static_cast<std::size_t>(rows) * columns * sizeof(double); When multiplying large values in C++, you need to think carefully about which type performs the multiplication — the result type of the assignment doesn't matter if overflow already happened on the right-hand side. Here, rows and columns are int, so rows * columns is computed as a 32-bit int multiplication. At their maximum of 100000100000 each, the product is 101010^{10}, which far exceeds the 32-bit int max of roughly 2.1×1092.1 \times 10^9 — overflow occurs before the result is ever stored. B is correct because casting rows to std::size_t (64-bit) before the first multiplication forces the entire chain — static_cast<std::size_t>(rows) * columns * sizeof(double) — to be evaluated in 64-bit arithmetic. C++ promotes the narrower operand upward, so columns and sizeof(double) both get promoted to std::size_t, and no intermediate result overflows. A fails because both rows * columns and that product multiplied by sizeof(double) happen as 32-bit int arithmetic — assigning to std::size_t only saves the final (already corrupted) value. C applies the cast after rows * columns is already computed as a 32-bit int, so the overflow has already occurred before static_cast runs. The cast then widens a garbage value. D promotes to unsigned int, which is still only 32 bits. The product 101010^{10} overflows 32-bit unsigned as well (max ≈ 4.3×1094.3 \times 10^9), so this doesn't solve the problem. Study tip: Always cast at least one operand to the wider type before the first multiplication — don't rely on the destination variable's type to prevent intermediate overflow.
  5. Which return type can represent every required result without overflow or loss of the sentinel?
    Answer: std::int64_t, using -1 for failure When choosing a return type for a search function, you need to satisfy two constraints simultaneously: the type must hold every valid index in your data set, and it must also represent your sentinel value exactly as intended. Here, valid indices range from 00 to 5,999,999,9995{,}999{,}999{,}999 (six billion records), and the sentinel is −1-1. std::int64_t is a signed 64-bit integer with a range of roughly −9.2×1018-9.2 \times 10^{18} to +9.2×1018+9.2 \times 10^{18}. It comfortably holds every index up to six billion and stores −1-1 literally and unambiguously — making D the correct answer. Each wrong choice fails one or both constraints. A (std::int32_t) is signed and does represent −1-1, but its maximum positive value is only 2,147,483,6472{,}147{,}483{,}647 — far short of six billion, so valid indices would overflow. B (std::uint32_t) fails doubly: it can't hold six billion (same ceiling as int32_t), and unsigned types cannot store −1-1 at all — assigning −1-1 wraps around to 4,294,967,2954{,}294{,}967{,}295, meaning your sentinel collides with a legitimate maximum value, not a reserved one. C (std::uint64_t) is wide enough for six billion, but assigning −1-1 to an unsigned type wraps to 18,446,744,073,709,551,61518{,}446{,}744{,}073{,}709{,}551{,}615. While technically usable as a convention, it requires callers to remember the wrap-around trick, introduces comparison pitfalls, and is semantically error-prone. A good rule of thumb: whenever a function must return either a non-negative count/index or a negative sentinel, reach for the signed 64-bit type — it's the natural, safe choice that avoids unsigned arithmetic surprises.
  6. Which statement correctly uses std::accumulate to calculate the total without overflowing an int accumulator?
    Answer: long long total = std::accumulate(v.begin(), v.end(), 0LL); When using std::accumulate, the type of the initial value you pass determines the accumulator's type throughout the entire summation — not the type of the variable you assign the result to. This is the core concept being tested here. In this problem, the vector holds 100000100000 values of 5000050000, so the true total is 5×1095 \times 10^9, which exceeds int's maximum of roughly 2.1×1092.1 \times 10^9. To avoid overflow, the accumulator itself must be long long from the very first addition. B is correct because 0LL is a long long literal. This forces std::accumulate to deduce its internal accumulator as long long, so every addition is performed in long long arithmetic — no overflow occurs. A fails because 0 is an int literal. Even though you assign the result to long long total, the accumulation happens entirely in int internally, overflowing before the final value is ever returned. The cast to long long happens after the damage is done. C makes the same mistake as A. Casting the result of an already-overflowed int computation to long long doesn't recover the lost data — you're just widening a corrupted value. D uses 0U, an unsigned int literal. While unsigned int avoids signed overflow, its maximum is only about 4.3×1094.3 \times 10^9, which is still too small for 5×1095 \times 10^9, so this can still produce a wrong result via unsigned wraparound. The key strategy: always match the initial value's type to the type you need for the accumulation — the return type assignment cannot save you from overflow that happens inside the function.
  7. Which expression computes the integer midpoint without overflowing and rounds downward in the same way as ordinary integer division?
    Answer: low + (high - low) / 2 Whenever you see an integer arithmetic question involving values near INT_MAX, your first instinct should be to check for overflow — the silent killer of midpoint calculations. The core insight: if low and high are both close to INT_MAX (roughly 2.1 billion for 32-bit signed integers), then low + high can exceed INT_MAX, causing undefined behavior through signed integer overflow. Any correct solution must avoid ever adding the two large values directly. C — low + (high - low) / 2 — is safe because high - low is guaranteed to be nonnegative and within range (since high >= low and both are nonnegative), so the subtraction never overflows. Dividing that difference by 2 and adding it back to low gives the correct midpoint, rounding down exactly as integer division does. A — (low + high) / 2 — is the classic trap. When both values are near INT_MAX, the addition overflows before division can save it. This produces a wrong or undefined result. B — low / 2 + high / 2 — avoids the large addition, but introduces a subtle rounding error. If both low and high are odd (e.g., 3 and 5), this gives 1+2=31 + 2 = 3 instead of the correct 44. The "lost" remainders don't combine properly. D — casting to double before dividing seems clever, but double only has 53 bits of mantissa precision. For large 32-bit integers, rounding errors in floating-point arithmetic can produce an incorrect result. The study tip: never add two potentially-large integers directly to find a midpoint — always restructure using subtraction first: low + (high - low) / 2.
  8. Which statement meets the requirement without integer overflow?
    Answer: double avg = (static_cast<long long>(a) + b) / 2.0; When working with arithmetic on 32-bit signed integers in C++, your first instinct should be to ask: can this overflow? A 32-bit int holds values up to roughly 2.1×1092.1 \times 10^9, so adding two large int values can silently wrap around, producing a garbage result before any cast or division occurs. Option B is correct because it widens a to long long before the addition: static_cast<long long>(a) + b. Since one operand is long long, the compiler promotes b to long long as well, giving you a 64-bit addition that cannot overflow for any pair of 32-bit inputs. Dividing by 2.0 then produces a double result, preserving the 0.50.5 fractional part when the sum is odd. Option A fails because a + b happens in 32-bit integer arithmetic first — the cast to double comes after the potentially overflowed result, so the damage is already done. Option C avoids overflow but loses precision: integer division truncates both halves, so inputs like a = 1, b = 0 yield 0.00.0 instead of 0.50.5. Option D uses float, which has only ~7 significant decimal digits of precision — far too little for large 32-bit integers that can have up to 10 digits, meaning the result can be silently rounded to an incorrect value. Study tip: Always cast before the operation that could overflow, not after. The cast must be applied to at least one operand so the compiler uses wider arithmetic for the entire expression.
  9. Which statement safely computes the product without allowing the multiplication itself to overflow?
    Answer: long long result = static_cast<long long>(a) * b; When multiplying two integers in C++, overflow happens at the moment the multiplication is performed — not when the result is stored. This means the types of the operands determine whether overflow occurs, regardless of what type the result is eventually assigned to. The maximum product here is 100000×100000=1010100000 \times 100000 = 10^{10}, which far exceeds the 32-bit signed int maximum of roughly 2.1×1092.1 \times 10^9. To prevent overflow, at least one operand must be promoted to a wider type before the multiplication executes. A is correct because static_cast<long long>(a) converts a to long long first. C++ then promotes b to long long as well (via implicit conversion), so the multiplication itself happens in 64-bit arithmetic — no overflow. B is the classic trap: the cast happens after a * b is evaluated. Both a and b are still int at the moment of multiplication, so overflow occurs before the cast even runs. Casting an already-overflowed value is undefined behavior, not a fix. C assigns the result to unsigned long long, but again, a * b is computed as int * int first. The assignment to a wider type comes too late — the overflow (and undefined behavior) has already occurred. D uses 1 * a * b, where 1 is an int literal, not a long long. Since 1, a, and b are all int, the multiplication still overflows in 32-bit arithmetic. Using 1LL instead would actually work, but as written, D fails. The key rule to remember: cast before you multiply, not after. Whenever you need a wider result, promote at least one operand explicitly before the operation, not the result afterward.
  10. Which code safely computes the squared distance for all permitted coordinates?
    Answer: std::int64_t dx = x1 - x2, dy = y1 - y2; std::int64_t d2 = dx * dx + dy * dy; Whenever you see arithmetic on int values being stored into a 64-bit type, ask yourself: where does the computation actually happen? The type of the destination variable doesn't matter — what matters is the type of the operands at the moment of multiplication. With coordinates ranging from −50000-50000 to 5000050000, the difference (x1−x2)(x1 - x2) can reach ±100000\pm100000. Squaring that gives 101010^{10}, which far exceeds int's maximum of roughly 2.1×1092.1 \times 10^9. So any multiplication performed in 32-bit int arithmetic will overflow, producing undefined behavior — even if you immediately store the result in a 64-bit variable afterward. D is correct because it converts dx and dy to std::int64_t before any multiplication occurs. Once the operands are 64-bit, all subsequent arithmetic happens in 64-bit, safely accommodating values up to 2×10102 \times 10^{10}. A fails because x1 - x2 and x1, x2 are all int, so the squaring and addition happen entirely in 32-bit arithmetic. Assigning the already-overflowed result to std::int64_t is too late. B makes the same mistake as A — the static_cast to int64_t wraps around an expression that has already overflowed in 32-bit arithmetic. Casting after undefined behavior doesn't rescue anything. C uses std::uint64_t, which avoids signed overflow, but converting a negative int difference like -100000 to an unsigned 64-bit integer produces a massive positive value (wrap-around), corrupting the result. Study tip: Always widen your operands before the multiplication, not after. A cast on the result is always too late.

Start this quiz