C / OPERATORS AND EXPRESSIONS
Arithmetic operators, division and the modulo trap
Predict C's integer division and remainder for negative operands, force real division when you need it, and wrap indices without going negative.
What you will learn
- Predict integer division for negatives: 7/2 is 3, -7/2 is -3, truncated toward zero
- Cast one operand to double when you need a fractional quotient, not the whole result
- Test parity with n % 2 != 0 so odd negative values are not misread as even
- Normalise a wrapped index with ((a % m) + m) % m before using it as a subscript
Understanding Arithmetic operators, division and the modulo trap
C's binary arithmetic operators are +, -, *, / and %, and only the last two hold surprises. The type of the result comes from the operands, not from where you store it: if both sides of a / are integers, it is integer division and the fractional part is discarded on the spot. So 7 / 2 is the int 3, and assigning that to a double can only ever give 3.0, because there is no fraction left to recover. Write 7 / 2.0, or cast one operand, and the usual arithmetic conversions turn the whole expression into a double division that yields 3.5.
Treat / and % as the two halves of one division rather than two unrelated operators. The standard ties them together: for a nonzero b and a representable quotient, (a/b)*b + a%b equals a. C discards the fraction toward zero — guaranteed since C99, implementation-defined before that — so -7 / 2 is -3 rather than -4, and the identity then forces -7 % 2 to be -1, since -3 * 2 + -1 == -7. The rule that falls out is that the remainder carries the sign of the left operand, while the divisor's sign only affects the quotient.
This is truncated division, not the floored modulo that Python or Ruby give you, and that difference is where the trap lives. Any code assuming % lands in 0..m-1 breaks the moment a negative value reaches it: n % 2 == 1 stops recognising odd numbers, and (i - 1) % n gives -1 instead of n - 1, which as an array subscript reads memory that is not yours. When negatives are possible, compare against != 0 instead of == 1, or normalise the remainder with ((a % m) + m) % m.
<stdio.h>
int main(void)
{
int a = 7, b = 2;
int n = -7, d = 2;
printf("7 / 2 = %d\n", a / b);
printf("7 %% 2 = %d\n", a % b);
printf("7 / 2.0 = %g\n", a / 2.0);
printf("-7 / 2 = %d\n", n / d);
printf("-7 %% 2 = %d\n", n % d);
printf("check: (n/d)*d + n%%d = %d\n", (n / d) * d + n % d);
printf("7 / -2 = %d\n", 7 / -2);
printf("7 %% -2 = %d\n", 7 % -2);
return 0;
}
In C, / and % are the two results of one truncating division: the quotient drops the fraction toward zero, so the remainder must take the sign of the left operand for (a/b)*b + a%b == a to hold.
Worked examples
Wrapping an index without going negative
Shows why a bare % cannot be used to cycle backwards through an array, and the two standard fixes.
<stdio.h>
static int wrap(int a, int m)
{
int r = a % m;
return r < 0 ? r + m : r;
}
int main(void)
{
const char *day[7] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
int today = 1;
for (int back = 0; back <= 3; back++) {
int raw = (today - back) % 7;
int fixed = wrap(today - back, 7);
printf("back %d: raw = %2d wrapped = %d (%s)\n",
back, raw, fixed, day[fixed]);
}
return 0;
}
Example explained
Line 1(today - back) % 7 gives -1 when the left operand is -1, because the remainder copies the dividend's sign and never wraps into 0..6 by itself.
Line 2day[raw] with raw = -1 would read one element before the array, so the bug appears as garbage or a crash rather than as a wrapped index.
Line 3wrap adds m back only when r is negative, which always lands in 0..m-1 because the magnitude of a % m is strictly less than m.
Line 4((a % m) + m) % m is the branchless equivalent and behaves identically for any positive m.
Truncation happens before the assignment
Demonstrates that the destination type cannot rescue an integer division, and uses / and % together to split a value.
<stdio.h>
int main(void)
{
int sum = 17, count = 5;
double wrong = sum / count;
double right = (double)sum / count;
printf("wrong = %.2f\n", wrong);
printf("right = %.2f\n", right);
int cents[2] = {1234, 1205};
for (int i = 0; i < 2; i++)
printf("%d cents = %d.%02d\n", cents[i], cents[i] / 100, cents[i] % 100);
return 0;
}
Example explained
Line 1sum / count is int divided by int, so it produces 3 and only then widens to 3.0; the type of wrong cannot reach back into the expression.
Line 2(double)sum / count converts one operand, so count is converted too and the division itself is done in floating point.
Line 3cents[i] / 100 and cents[i] % 100 are the quotient and remainder of the same division: whole units and leftover units.
Line 4%02d is required because the remainder 5 must print as 05, otherwise 1205 cents would read as 12.5.
Important notes
% accepts integer operands only; for doubles use fmod(x, y) from <math.h>, which also truncates toward zero and so keeps the sign of x. Linking may need -lm.
Integer / or % with a zero right operand is undefined behaviour, not an error value: on x86 it raises SIGFPE and kills the process, and INT_MIN / -1 overflows the same way, so guard the divisor yourself.
Common mistakes
Writing double avg = total / count; with two ints: 17/5 truncates to 3 before the conversion, so avg is 3.00 and the bug hides behind a plausible-looking double.
Using if (n % 2 == 1) as an odd test: -3 % 2 is -1, so every odd negative number silently takes the even branch.
Indexing with buf[(pos - 1) % size]: when pos is 0 the subscript is -1, reading outside the buffer, and undefined behaviour often returns believable garbage instead of crashing.
Try it yourself
Change, predict, then run
Write int digit_sum(int n) that loops taking n % 10 and then n /= 10, and print digit_sum(1234) and digit_sum(-1234). Explain why the second call gives -10 and change the function so both inputs return 10.
Open the C workspaceCheck your understanding
For int a = -9, b = 4; what do a / b and a % b evaluate to in C, and why?
- -3 and 3, because integer division rounds down and a remainder is never negative
- -2 and 3, because the remainder takes the sign of the divisor
- -2 and -1, because / truncates toward zero and the remainder takes the sign of the dividend
- -3 and -1, because / rounds down while % follows the dividend
Show answer
-9/4 is -2.25; C discards the fraction rather than rounding down, giving -2, and the identity (a/b)*b + a%b == a then forces the remainder to be -9 - (-8) = -1. Option 0 is the floored result that Python produces, but C does not floor, and option 3 is internally inconsistent: -3 * 4 + -1 is -13, not -9.