◐ ai-teacher

🔧 C Programming · Chapter 3

Operators, integer division and overflow

In C, 7 / 2 is 3, -1 is not less than 1u, and 2.5 billion fits in nothing you declared. Arithmetic does exactly what the types say — not what you meant.

Study this lesson free in the app →

The basics

Operators are + - * / %, comparisons == != < >, logic && || !, and assignment = += ++. In C, every one of them works on the types of its operands, and the result has a type too.

The problem it solves: knowing the rules is how you stop "my average is always a whole number" and "the total went negative" bugs before they ship.

The mental model: the CPU does integer maths and floating-point maths with different instructions. C picks one based on the types:

Words you must know 4

truncation
dropping the fractional part: 3.9 → 3, −3.9 → −3
cast
(double)x converts a value to another type
overflow
a result outside the type's range
precedence
which operator binds first (* before +, == before &)

Explained

int done = 7, total = 2;
double avg = done / total;   // 3.0, not 3.5

The compiler didn't make a mistake. It did integer division because both sides were int, then stored the 3 in a double. Every C programmer writes this bug once. This lesson makes sure it's in practice, not in production.

Sharing laddoos

7 laddoos, 2 children, and a strict rule: no breaking laddoos.

Each child gets 3, and 1 is left on the plate. That's 7 / 2 == 3 and 7 % 2 == 1: integer division and remainder. Division answers "how many whole ones each"; % answers "what's left".

If you're allowed to break them (a knife = double), each gets 3.5.

And the odometer on an old Maruti: at 99999 km, one more km shows 00000. That's unsigned overflow: it wraps. C's signed integers don't promise even that much.

The rules, precisely

Division and remainder (integers) | Expression | Result | Why | | --- | --- | --- | | 7 / 2 | 3 | fraction dropped | | -7 / 2 | -3 | truncates toward zero (since C99) | | -7 % 2 | -1 | sign follows the left operand | | 7.0 / 2 | 3.5 | one double → real division | | (double)7 / 2 | 3.5 | cast first, then divide | | (double)(7 / 2) | 3.0 | divide first (3), then cast — too late |

Integer division by zero (x / 0, x % 0) is undefined behaviour. On Linux it usually crashes with "Floating point exception".

Ranges (<limits.h>): INT_MAX = 2,147,483,647 · UINT_MAX = 4,294,967,295 · LLONG_MAX ≈ 9.22 × 10¹⁸.

Diwali night, 11:40 PM

A Bengaluru ride app sums the day's fares in paise into an int:

Around ride 85,900 the running total passes INT_MAX. Signed overflow is undefined behaviour. In practice on x86 it usually wraps to about −2.1 billion, and the finance dashboard shows a negative day.

Fix: int64_t total (max ≈ 9.2 × 10¹⁸). And watch for the hidden version: int a = 50000, b = 50000; long long c = a * b; still overflows, because a * b is computed in int before it is stored. Write (long long)a * b.

Unsigned wrap, and the comparison trap

#include <stdio.h>
#include <limits.h>

int main(void) {
    unsigned int u = 0;
    u = u - 1;
    printf("%u\n", u);            // 4294967295 — wrapped, well-defined

    unsigned int max = UINT_MAX;
    printf("%u\n", max + 1);      // 0

    int i = -1;
    unsigned int one = 1;
    if (i < one) printf("less\n");
    else         printf("NOT less\n");   // prints NOT less
    return 0;
}

When a signed and an unsigned int meet, the signed one is converted to unsigned. −1 becomes 4,294,967,295, which is not less than 1. sizeof returns unsigned too, so if (-1 < sizeof(int)) is false. -Wall -Wextra warns: comparison of integer expressions of different signedness.

Precedence ambush

int x = 6;
if (x & 1 == 0)
    printf("even\n");
else
    printf("odd\n");

Gotchas

The types decide the maths, and the right side is computed before it is stored.

int / int drops the fraction. Signed overflow is undefined behaviour; unsigned wraps. Signed meets unsigned → signed becomes unsigned. Parenthesise bitwise expressions.

Words to know

Integer divisionDivision of two integers; the fractional part is discarded (truncated toward zero).
Modulo / remainder (%)What's left after integer division. Its sign follows the left operand in C.
CastAn explicit conversion written (type)value, e.g. (double)total.
OverflowA result outside the range of its type. Wraps for unsigned; undefined behaviour for signed.
Usual arithmetic conversionsThe rules that bring two operands to a common type before an operation, e.g. int + double → double.
PrecedenceWhich operator applies first when there are no parentheses.
Short-circuit evaluation&& stops at the first false, || at the first true; the rest isn't evaluated.

In two minutes

Test yourself

1. What is 17 / 5 in C (both int)?

  1. 3.4
  2. 3
  3. 4
  4. 3.0
Show the answer

3 — int / int is integer division: the 0.4 is discarded. It doesn't round, so it's not 4.

2. What is -17 % 5?

  1. 3
  2. -2
  3. 2
  4. -3
Show the answer

-2 — -17 / 5 truncates toward zero to -3; -3 × 5 = -15; -17 - (-15) = -2. In C the remainder takes the sign of the left operand. Python's -17 % 5 would be 3.

3. int a = 3, b = 4; double r = a / b; What is r?

  1. 0.75
  2. 0.0
  3. 1.0
  4. Compile error
Show the answer

0.0 — a / b is computed as int (0) first, then converted to 0.0. Write (double)a / b.

4. Which correctly computes a percentage of completed rides as a double?

  1. (double)(done / total) * 100
  2. done / total * 100.0
  3. 100.0 * done / total
  4. (int)done / total * 100
Show the answer

100.0 * done / total — 100.0 * done is double first, so the division is real. The first two still do int / int before anything becomes double.

5. unsigned int u = 0; u--; What is u (32-bit unsigned)?

  1. -1
  2. 0
  3. 4294967295
  4. Undefined behaviour
Show the answer

4294967295 — Unsigned arithmetic is defined to wrap modulo 2³², so 0 - 1 = UINT_MAX. Only signed overflow is undefined behaviour.

6. int x = INT_MAX; x = x + 1; What does the C standard say?

  1. x becomes INT_MIN
  2. x becomes 0
  3. Undefined behaviour
  4. Compile error
Show the answer

Undefined behaviour — Signed overflow is undefined behaviour. It often wraps to INT_MIN in practice, but optimisers may assume it never happens and remove your overflow checks, so you can't rely on it.

There are 10 questions, 3 puzzles and 5 recall cards for this lesson in the app — the recall cards come back on later days, just before you would forget.

Learn it so it stays. Read it, test yourself, then recall it on two different days. The app schedules that for you, free.

Start this lesson →