Avoiding Integer Overflow - C Plus Plus
- 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 measurements, each at most , so the worst-case total is . That's five billion — now check each type against that ceiling.std::int64_t(C) is the correct answer. Its maximum exceeds , which comfortably holds . No overflow is possible. Choice A,std::int32_t, fails because its maximum is only — 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 — 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 — 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. - 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 , the product , which far exceeds the 32-bit signed maximum of roughly . Any multiplication that stays inint32_twill overflow, producing undefined behavior and a garbage result. Choice C,static_cast<std::int64_t>(n) * (n - 1) / 2, is correct because castingnto 64-bit before the multiplication forces the entire expression into 64-bit arithmetic. The compiler promotes(n - 1)toint64_tas well, so the product is computed safely, and the final result fits comfortably inint64_t. Choice A performs the entire multiplication in 32-bit (n * (n - 1)staysint32_t) and only stores the already-overflowed result intoint64_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 inint32_t, which truncates the large value and still produces wrong output for bign. 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. - 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 . For 100,000 days, the result is , which far exceeds a 32-bit unsigned integer's maximum of about . 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 castingdaystostd::uint64_tbefore 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 chaindays * 24 * 60 * 60 * 1000is 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 tostd::uint64_thappens after the entire expression is evaluated in 32-bit, so it only widens an already-corrupted value. D is wrong because1000ULLwidens only the last multiplication (... * 60 * 1000). Earlier multiplications, likedays * 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. - 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,rowsandcolumnsareint, sorows * columnsis computed as a 32-bitintmultiplication. At their maximum of each, the product is , which far exceeds the 32-bitintmax of roughly — overflow occurs before the result is ever stored. B is correct because castingrowstostd::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, socolumnsandsizeof(double)both get promoted tostd::size_t, and no intermediate result overflows. A fails because bothrows * columnsand that product multiplied bysizeof(double)happen as 32-bitintarithmetic — assigning tostd::size_tonly saves the final (already corrupted) value. C applies the cast afterrows * columnsis already computed as a 32-bitint, so the overflow has already occurred beforestatic_castruns. The cast then widens a garbage value. D promotes tounsigned int, which is still only 32 bits. The product overflows 32-bit unsigned as well (max ≈ ), 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. - Which return type can represent every required result without overflow or loss of the sentinel? Answer:
std::int64_t, using-1for 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 to (six billion records), and the sentinel is .std::int64_tis a signed 64-bit integer with a range of roughly to . It comfortably holds every index up to six billion and stores 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 , but its maximum positive value is only — far short of six billion, so valid indices would overflow. B (std::uint32_t) fails doubly: it can't hold six billion (same ceiling asint32_t), and unsigned types cannot store at all — assigning wraps around to , 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 to an unsigned type wraps to . 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. - Which statement correctly uses
std::accumulateto calculate the total without overflowing anintaccumulator?Answer:long long total = std::accumulate(v.begin(), v.end(), 0LL);When usingstd::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 values of , so the true total is , which exceedsint's maximum of roughly . To avoid overflow, the accumulator itself must belong longfrom the very first addition. B is correct because0LLis along longliteral. This forcesstd::accumulateto deduce its internal accumulator aslong long, so every addition is performed inlong longarithmetic — no overflow occurs. A fails because0is anintliteral. Even though you assign the result tolong long total, the accumulation happens entirely inintinternally, overflowing before the final value is ever returned. The cast tolong longhappens after the damage is done. C makes the same mistake as A. Casting the result of an already-overflowedintcomputation tolong longdoesn't recover the lost data — you're just widening a corrupted value. D uses0U, anunsigned intliteral. Whileunsigned intavoids signed overflow, its maximum is only about , which is still too small for , 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. - Which expression computes the integer midpoint without overflowing and rounds downward in the same way as ordinary integer division? Answer:
low + (high - low) / 2Whenever you see an integer arithmetic question involving values nearINT_MAX, your first instinct should be to check for overflow — the silent killer of midpoint calculations. The core insight: iflowandhighare both close toINT_MAX(roughly 2.1 billion for 32-bit signed integers), thenlow + highcan exceedINT_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 becausehigh - lowis guaranteed to be nonnegative and within range (sincehigh >= lowand both are nonnegative), so the subtraction never overflows. Dividing that difference by 2 and adding it back tolowgives the correct midpoint, rounding down exactly as integer division does. A —(low + high) / 2— is the classic trap. When both values are nearINT_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 bothlowandhighare odd (e.g., 3 and 5), this gives instead of the correct . The "lost" remainders don't combine properly. D — casting todoublebefore dividing seems clever, butdoubleonly 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. - 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-bitintholds values up to roughly , so adding two largeintvalues can silently wrap around, producing a garbage result before any cast or division occurs. Option B is correct because it widensatolong longbefore the addition:static_cast<long long>(a) + b. Since one operand islong long, the compiler promotesbtolong longas well, giving you a 64-bit addition that cannot overflow for any pair of 32-bit inputs. Dividing by2.0then produces adoubleresult, preserving the fractional part when the sum is odd. Option A fails becausea + bhappens in 32-bit integer arithmetic first — the cast todoublecomes 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 likea = 1, b = 0yield instead of . Option D usesfloat, 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. - 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 , which far exceeds the 32-bit signedintmaximum of roughly . To prevent overflow, at least one operand must be promoted to a wider type before the multiplication executes. A is correct becausestatic_cast<long long>(a)convertsatolong longfirst. C++ then promotesbtolong longas well (via implicit conversion), so the multiplication itself happens in 64-bit arithmetic — no overflow. B is the classic trap: the cast happens aftera * bis evaluated. Bothaandbare stillintat 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 tounsigned long long, but again,a * bis computed asint * intfirst. The assignment to a wider type comes too late — the overflow (and undefined behavior) has already occurred. D uses1 * a * b, where1is anintliteral, not along long. Since1,a, andbare allint, the multiplication still overflows in 32-bit arithmetic. Using1LLinstead 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. - 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 onintvalues 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 to , the difference can reach . Squaring that gives , which far exceedsint's maximum of roughly . So any multiplication performed in 32-bitintarithmetic will overflow, producing undefined behavior — even if you immediately store the result in a 64-bit variable afterward. D is correct because it convertsdxanddytostd::int64_tbefore any multiplication occurs. Once the operands are 64-bit, all subsequent arithmetic happens in 64-bit, safely accommodating values up to . A fails becausex1 - x2andx1,x2are allint, so the squaring and addition happen entirely in 32-bit arithmetic. Assigning the already-overflowed result tostd::int64_tis too late. B makes the same mistake as A — thestatic_casttoint64_twraps around an expression that has already overflowed in 32-bit arithmetic. Casting after undefined behavior doesn't rescue anything. C usesstd::uint64_t, which avoids signed overflow, but converting a negativeintdifference like-100000to 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.