Skip to content

94 Advanced The Hundred

The Division That Crashed

no zeros were harmed in this divide-by-zero

int scale(int value, int factor) {
    if (factor == 0)
        return 0;   // the only divisor that can hurt us... right?
    return value / factor;
}

int main(int argc, char** argv) {
    int value = (argc > 1) ? std::atoi(argv[1]) : INT_MIN;
    int factor = (argc > 2) ? std::atoi(argv[2]) : -1;
    std::cout << "scale(" << value << ", " << factor << ") = " << scale(value, factor) << "\n";
}

Run it. The divisor is -1, not 0 — what does it print?

Answer

Nothing at all. Typical behavior (GCC 11.5 on x86-64) — the process is killed mid-statement:

$ ./demo
Floating point exception (core dumped)

$? is 136 — killed by signal 8, SIGFPE. Any other pair is fine (./demo 100 -1 prints scale(100, -1) = -100), and the default pair dies identically at -O0 through -O3.

Why

int is lopsided: it runs from -2147483648 to 2147483647, so the result this division calls for — positive 2147483648 — has no representation. That makes it signed overflow, which is undefined behavior; but where entry 93 was the optimizer assuming it can't happen, this is the CPU noticing that it did. x86-64 has one signed integer divide instruction, idiv, and it traps on two inputs: a zero divisor, and the one quotient too large to store — Linux delivers that trap as SIGFPE, so a program with no floating point and no zero in it dies of a "floating point exception", its half-built output line still unflushed in the stream buffer. INT_MIN % -1 hits the same instruction and dies the same way; std::abs(INT_MIN) is undefined for the same missing-value reason (GCC 11.5 hands back INT_MIN, still negative). -Wall -Wextra say nothing here; GCC's default -Woverflow catches only the all-literal form (int x = INT_MIN / -1;), and if those constants arrive through variables instead, -O2 folds the division at compile time and cheerfully prints -2147483648 while -O0 crashes. -fsanitize=undefined names it: "runtime error: division of -2147483648 by -1 cannot be represented in type 'int'".

The fix

Guard the second bad pair exactly like the first, or divide in a type that holds the answer:

if (factor == 0 || (value == INT_MIN && factor == -1))
    return 0;   // the pair that traps, handled like the pair that divides by zero

long long wide = static_cast<long long>(value) / factor;   // 2147483648 fits in 64 bits

-fwrapv won't rescue you: it defines wrapping for +, - and *; idiv never got the memo.

Takeaway: exactly one integer division can overflow, and it doesn't wrap — it traps.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo — same crash at every -O level.

Open in Compiler Explorer ↗ Quiz this entry