Learn C Programming

Lesson 1 of 8 · Operators and Type Conversion

Module 4 · Operators and Type Conversion

Arithmetic, Integer Division and the Modulo Trap

FreeReading

In this lesson

  • Use +, -, *, / and % on both integers and doubles.
  • Say which of two divisions C will do, by looking only at the operands.
  • Predict the sign of a remainder before you run the program.

David reads three marks, adds them and divides by 3. The marks are 75, 82 and 82. His program prints 79.

He checks the sum on paper: 239. He checks the division: 79.67. The program is not wrong about the marks and it is not rounding. It is doing a different division from the one he meant.

Zara looks at the line for two seconds and says "all three of those are int". That is the whole lesson.

Five operators, and only one of them is strange

C has five arithmetic operators. Four of them behave the way school taught you.

The five arithmetic operators

a + b     sum
a - b     difference
a * b     product
a / b     quotient        integer division when both sides are integers
a % b     remainder       integers only, never a double
  • % is called the modulo or remainder operator. It gives what is left over.
  • % refuses doubles. 7.5 % 2 does not compile.
  • There is no power operator in C. 2 ^ 8 is not 256, and lesson 4 says what it really is.

Every one of them takes two values and produces a third. Nothing is stored until you write an =.

#include <stdio.h>

int main(void)
{
    int a = 17;
    int b = 5;

    printf("sum        %d\n", a + b);
    printf("difference %d\n", a - b);
    printf("product    %d\n", a * b);
    printf("quotient   %d\n", a / b);
    printf("remainder  %d\n", a % b);
    return 0;
}
sum        22
difference 12
product    85
quotient   3
remainder  2

Read the last two lines together. 17 is three fives with 2 left over, and C hands you the two halves of that sentence separately.

The operands decide the division, not the box

This is the rule David needed. C looks at the two values beside the / and nothing else.

If both are integers, C does integer division: it divides and throws the fraction away. If either one is a double, C does real division.

The type of the variable you assign the answer to has no say in it. By the time the assignment happens, the fraction is already gone.

#include <stdio.h>

int main(void)
{
    int total = 239;

    double wrong = total / 3;
    double right = total / 3.0;

    printf("wrong %.2f\n", wrong);
    printf("right %.2f\n", right);
    return 0;
}
wrong 79.00
right 79.67

Both lines store into a double. Only the second one ever had a fraction to store.

So the fix is never on the left of the =. It is on the right, and it is one character: write 3.0, or cast one operand, which lesson 6 covers properly.

Integer division truncates toward zero

"Throws the fraction away" needs one more word when the answer is negative.

C does not round down. It cuts toward zero. So 7 / 2 is 3, and -7 / 2 is -3, not -4.

Integer division cuts toward zero, from both sides Both answers move toward zero, never toward minus infinity 0 -4 -3 -2 2 3 4 -3.5 -7 / 2 is -3 3.5 7 / 2 is 3

This was settled by C99. Older C left the negative case to the compiler, and you may still meet books that say so.

So integer division is not "round down". It is "drop the fraction", and those two sentences disagree on every negative number.

The remainder takes the sign of the left operand

Once division cuts toward zero, the sign of % follows from arithmetic. C guarantees that (a / b) * b + a % b equals a.

Work that backwards and the rule falls out: a % b has the sign of a, whatever b is.

ExpressionQuotientRemainderSign of the remainder
7 / 3, 7 % 321follows 7
-7 / 3, -7 % 3-2-1follows -7
7 / -3, 7 % -3-21follows 7
-7 / -3, -7 % -32-1follows -7
#include <stdio.h>

int main(void)
{
    printf("  7 / 3 = %2d    7 %% 3 = %2d\n", 7 / 3, 7 % 3);
    printf(" -7 / 3 = %2d   -7 %% 3 = %2d\n", -7 / 3, -7 % 3);
    printf("  7 / -3 = %2d   7 %% -3 = %2d\n", 7 / -3, 7 % -3);
    printf(" -7 / -3 = %2d  -7 %% -3 = %2d\n", -7 / -3, -7 % -3);
    return 0;
}
  7 / 3 =  2    7 % 3 =  1
 -7 / 3 = -2   -7 % 3 = -1
  7 / -3 = -2   7 % -3 =  1
 -7 / -3 =  2  -7 % -3 = -1

%% inside a format string prints one percent sign. A single % there would start a specifier.

Read the second row twice. -7 % 3 is -1, and many languages answer 2 instead. Python does. C does not.

So a "wrap this number into the range 0 to 2" line written from Python habit is broken in C for negative input.

Digits, with nothing but / and %

These two operators together take a number apart. % 10 gives the last digit; / 10 removes it.

#include <stdio.h>

int main(void)
{
    int code = 4728;

    printf("last digit      %d\n", code % 10);
    printf("tens digit      %d\n", code / 10 % 10);
    printf("without the last %d\n", code / 10);
    printf("first digit     %d\n", code / 1000);
    return 0;
}
last digit      8
tens digit      2
without the last 472
first digit     4

The tens line reads left to right: divide by 10 first, then take the remainder. / and % have equal precedence, so they run in written order.

So digit work needs no loop and no string. Module 6 will loop over any number of digits; today the count is fixed and known.

Dividing by zero, twice, and neither is a rule

Integer division by zero is undefined behaviour. The standard places no requirement on the program at all.

On the Playground the program stops before printing and the run reports a failure. That is one thing it may do, not something to rely on.

Floating point is a different story. The Playground's compiler follows IEEE 754, where dividing by zero produces infinity.

#include <stdio.h>

int main(void)
{
    double zero = 0.0;

    printf("%f\n", 1.0 / zero);
    printf("%f\n", -1.0 / zero);
    return 0;
}
inf
-inf

Zero divided by zero gives nan instead, which means "not a number". Its printed spelling varies between libraries, so no output block here claims one.

So the honest summary is: guard the divisor yourself. Neither behaviour is a feature you should design around.

Example 1: the smallest program that shows the two divisions

One pair of numbers, divided twice, with one character different.

#include <stdio.h>

int main(void)
{
    printf("%d\n", 9 / 4);
    printf("%.2f\n", 9 / 4.0);
    return 0;
}
2
2.25

The first line is not a rounded version of the second. It is a different operation that happens to share a symbol.

Run in Compiler
Example 2: minutes into hours and minutes

The classic pair. / gives the whole part, % gives what is left.

#include <stdio.h>

int main(void)
{
    int minutes = 0;
    scanf("%d", &minutes);

    int hours = minutes / 60;
    int left = minutes % 60;

    printf("%d h %d m\n", hours, left);
    return 0;
}
3 h 25 m

That output is for the input 205. Three whole hours is 180 minutes, and 25 minutes do not make a fourth.

Run in Compiler
Example 3: David's average, written the way he meant it

Three marks in, one average out, printed to two decimal places.

#include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 0;
    int c = 0;
    scanf("%d %d %d", &a, &b, &c);

    int total = a + b + c;
    double average = total / 3.0;

    printf("total   %d\n", total);
    printf("average %.2f\n", average);
    return 0;
}
total   239
average 79.67

That output is for the input 75 82 82. The sum stays an int because marks are whole. Only the division needs a real number, and 3.0 is the one that provides it.

Run in Compiler

Where this is used

  • A clock. The Linux kernel turns a count of seconds into hours, minutes and seconds with exactly the / and % pair of Example 2. Every timestamp you read started as one big number.
  • Pagination. A results page showing 20 rows finds the last page with (total + 19) / 20. Integer division truncating is what makes that trick work.
  • The ISBN check digit. The last digit of a book number is computed with a weighted sum and % 11. A scanner rejects a mistyped number before any database is asked.
  • Odd or even. A game engine alternates behaviour on frame % 2, and SQLite picks a hash bucket with % on a table size. Both are the remainder doing the job a loop would otherwise do.

Common mistakes

1. Fixing the division by changing the box.

double average = (75 + 82 + 82) / 3;
printf("%.2f\n", average);

No message at all, at either command line, and it prints 79.00. The division finished before the double was involved. Change 3 to 3.0.

2. Asking % for the remainder of a decimal.

double price = 7.5;
printf("%f\n", price % 2);

GCC 12 says error: invalid operands to binary % (have 'double' and 'int'). This one is refused at compile time, which is a kindness. Use fmod from <math.h> if you really need it.

3. Expecting a positive remainder from a negative number.

int index = -1;
printf("%d\n", index % 7);

No message at either command line, and it prints -1. Python answers 6 for the same line. Silence is the problem here: the wrong answer looks correct until the input goes negative.

4. Writing 2 ^ 8 for two to the power eight.

printf("%d\n", 2 ^ 8);

The Playground's GCC 12 says nothing here, and the program builds and prints 10. GCC 13 and newer catch it with no flags at all: warning: result of '2^8' is 10; did you mean '1 << 8' (256)?. C has no power operator; ^ is the bitwise exclusive or of lesson 4.

Brain teaser

David expects 7.0, because he divided by 2 and then multiplied by 2.

#include <stdio.h>

int main(void)
{
    printf("%.1f\n", 7 / 2 * 2.0);
    return 0;
}

It prints 6.0. Say exactly which operation happened first and what its value was. Then answer the harder half: move the 2.0 to the front, as 2.0 * 7 / 2, and say what changes and why.

* and / have equal precedence, so read strictly left to right. Ask what types sit beside the first operator you meet.

Exercise 1Easy

Maria is checking a three digit locker code and wants its digits printed separately.

Input. One line with one integer n.

Output. One line with the three digits, separated by single spaces.

Constraints. 100 <= n <= 999.

Sample. Input 407 gives 4 0 7.

#include <stdio.h>

int main(void)
{
    int n = 0;
    scanf("%d", &n);

    /* Hundreds with /, units with %, and the middle one needs both. */

    return 0;
}

Graded as digit-split. No loop and no array; three expressions is the whole program.

Run in Compiler
Exercise 2Easy

Kenji stores every video length in whole minutes and wants it shown as hours and minutes.

Input. One line with one integer m, a number of minutes.

Output. One line: the hours, one space, then the minutes left over.

Constraints. 0 <= m <= 1000000.

Sample. Input 205 gives 3 25.

#include <stdio.h>

int main(void)
{
    int m = 0;
    scanf("%d", &m);

    /* Example 2, with the output shape the statement asks for. */

    return 0;
}

Not graded in this module. Check 0 and 60 yourself; both have a zero in the answer.

Run in Compiler
Exercise 3Medium

Amara collects four marks and wants their average, not their whole part.

Input. One line with four integers.

Output. One line with the average, to exactly two decimal places.

Constraints. Each mark is between 0 and 100.

Sample. Input 75 82 82 90 gives 82.25.

#include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 0;
    int c = 0;
    int d = 0;
    scanf("%d %d %d %d", &a, &b, &c, &d);

    /* Add as integers. Divide by a number that has a decimal point. */

    return 0;
}

Not graded in this module. Try 4 and then 4.0 and watch one input change the answer.

Run in Compiler
Exercise 4Medium

Zara is testing a scheduler and needs to know whether one number divides another exactly.

Input. One line with two integers n and d.

Output. One line with 1 if d divides n exactly, otherwise 0.

Constraints. -1000000000 <= n <= 1000000000, and 1 <= d <= 1000000000.

Sample. Input 91 7 gives 1. Input -91 6 gives 0.

#include <stdio.h>

int main(void)
{
    int n = 0;
    int d = 0;
    scanf("%d %d", &n, &d);

    /* A comparison is worth 1 or 0, so print it with %d. No if needed. */

    return 0;
}

Graded as is-divisible. Negative n is in the hidden tests, so decide what you compare the remainder against.

Run in Compiler

Common doubts

  • Why does C not just give me the exact answer?

    Because integer division is a different, faster machine instruction, and most programs want the whole part. C makes you ask for the other one.

  • Is a / b * b always a?

    Not for integers. 7 / 2 * 2 is 6. The fraction was thrown away before the multiplication ever saw it.

  • How do I get a remainder that is never negative?

    Write (n % d + d) % d. The first remainder may be negative, adding d lifts it, and the second % brings it back into range.

  • Can I use % with a long long?

    Yes. % works on every integer type, including char and unsigned. Only floating types are refused.

  • Does % cost more than *?

    On most processors division and remainder are the slowest arithmetic there is. It matters only inside a very hot loop, and Module 16 returns to it.

Key takeaways

  • C has five arithmetic operators, and % works on integers only.
  • The operands decide whether / is integer division; the target type has no say.
  • Integer division cuts toward zero, so -7 / 2 is -3.
  • a % b takes the sign of a, which is not what Python does.
  • % 10 and / 10 take a number apart digit by digit.
  • Integer division by zero is undefined behaviour; double division by zero gives inf here.

Next you stop calculating with numbers and start comparing them, and the answer to a comparison turns out to be a number too.

End of lesson 1

Mark it done, and your progress moves with you.

Next: Relational and Logical Operators, and Short Circuit