Arithmetic and expressions
Explore C's arithmetic operators, integer vs floating-point division, operator precedence, and the modulo operator.
- Use the five arithmetic operators correctly
- Explain the difference between integer division and floating-point division
- Apply the modulo operator to extract remainders
- Describe C operator precedence and use parentheses to control evaluation order
Programs are machines for transforming data. Before you can do anything interesting — convert temperatures, calculate distances, decide which branch to take — you need to understand how C evaluates expressions. C's arithmetic is mostly familiar, but a few corners are different enough from how you might expect them to work that they trip up beginners.
The five arithmetic operators
#include <stdio.h>
int main(void) {
int a = 17, b = 5;
printf("%d + %d = %d\n", a, b, a + b); /* 22 */
printf("%d - %d = %d\n", a, b, a - b); /* 12 */
printf("%d * %d = %d\n", a, b, a * b); /* 85 */
printf("%d / %d = %d\n", a, b, a / b); /* 3 -- integer division */
printf("%d %% %d = %d\n", a, b, a % b); /* 2 -- remainder */
return 0;
}The %% in the format string prints a literal % character. All five operators are binary — they take two operands.
Integer division truncates
The most important thing to understand about / with integer operands: it truncates toward zero. It does not round — it drops the fractional part entirely.
int result = 17 / 5; /* 3, not 3.4 */
int result2 = -7 / 2; /* -3, not -3.5 (truncates toward zero) */This is intentional: integer division is exact arithmetic in the integer domain. But it bites beginners who expect 17/5 to give 3.4.
To get a decimal result, at least one operand must be a double or float:
double result = 17.0 / 5; /* 3.4 */
double result2 = (double)17 / 5; /* 3.4 -- cast converts 17 to 17.0 */The (double) syntax is a cast — it explicitly converts an integer to a floating-point type for this expression. The original variable is unchanged.
The modulo operator
% gives the remainder after integer division. It is only defined for integer types.
17 % 5 == 2 /* 17 = 3 * 5 + 2 */
10 % 3 == 1 /* 10 = 3 * 3 + 1 */
12 % 4 == 0 /* 12 = 3 * 4 + 0 -- evenly divisible */Classic uses: checking if a number is even (n % 2 == 0), wrapping an index around a fixed-size buffer (index % size), and extracting individual digits from a number (n % 10 gives the last digit).
Compound assignment operators
Writing x = x + 5 is so common that C provides shorthand:
int x = 10;
x += 5; /* x = x + 5 → 15 */
x -= 3; /* x = x - 3 → 12 */
x *= 2; /* x = x * 2 → 24 */
x /= 4; /* x = x / 4 → 6 */
x %= 4; /* x = x % 4 → 2 */Increment and decrement
C has dedicated operators for adding or subtracting 1:
int n = 5;
n++; /* n is now 6 -- postfix increment */
++n; /* n is now 7 -- prefix increment */
n--; /* n is now 6 -- postfix decrement */
--n; /* n is now 5 -- prefix decrement */The difference between prefix and postfix matters in expressions: ++n increments first and returns the new value, while n++ returns the old value and then increments. In isolation (as a standalone statement) they are equivalent. In complex expressions, the prefix form is clearer.
Operator precedence
When an expression mixes operators, C evaluates them in a specific order — just like PEMDAS in mathematics. The most important rules:
*,/,%bind more tightly than+and-.- Operators at the same precedence level evaluate left to right (for most operators).
- Parentheses always take the highest precedence.
int result = 3 + 4 * 2; /* 11, not 14: * before + */
int result2 = (3 + 4) * 2; /* 14: parentheses first */
int result3 = 10 - 3 - 2; /* 5: left-to-right: (10-3)-2 */When in doubt, add parentheses. Code that relies on subtle precedence rules is hard to read and easy to get wrong. If a reader of your code — including your future self — has to think about whether a + b * c / d - e evaluates as expected, add parentheses to make it unambiguous.
Overflow
Integer types have fixed sizes and therefore fixed ranges. What happens when you exceed the range?
int max = 2147483647; /* INT_MAX, from <limits.h> */
int overflow = max + 1; /* undefined behaviour for signed ints */For signed integers, overflow is undefined behaviour in C. The compiler is allowed to assume it never happens, which means it can optimise away overflow checks. For unsigned integers, overflow wraps around: UINT_MAX + 1 == 0.
Signed integer overflow is undefined behaviour. This means the compiler can produce any result, including results that are not simply wrong but are actively surprising — the compiler may eliminate branches that depend on the overflow. Use <limits.h> constants to check before you overflow, not after.
A complete example
#include <stdio.h>
int main(void) {
/* Celsius to Fahrenheit: F = (C * 9 / 5) + 32 */
/* Using integer arithmetic -- loses precision */
int celsius = 100;
int fahrenheit_int = (celsius * 9 / 5) + 32;
printf("Integer: %d C = %d F\n", celsius, fahrenheit_int);
/* Using floating-point arithmetic -- exact */
double c = 100.0;
double f = (c * 9.0 / 5.0) + 32.0;
printf("Double: %.1f C = %.1f F\n", c, f);
return 0;
}Output:
Integer: 100 C = 212 F
Double: 100.0 C = 212.0 FFor 100°C the answers match, but try 37°C and you will see the integer truncation.
Where to go next
You can now compute values in C. Next: the temperature converter lab — you will put variables, types, and arithmetic together to build a complete program that converts between temperature scales.
Variables and primitive types
Learn how C stores data with int, char, float, and double — including sizes, ranges, and how to print values with printf format specifiers.
Lab: Temperature converter
Build a command-line temperature converter that converts between Celsius, Fahrenheit, and Kelvin using what you have learned so far.