Skip to content

Computation

Computations are typically done through functions. So far, we have only shown one function which is the main function int main(void). As a good practice (and is actually enforced in a strict ANSI C standard), a function body has two parts:

  1. Declarations: This part contains all the variables to be used by the function. It tells the compiler what type of memory cells are needed.
  2. Executables: This part contains the instructions on how to process the memory cells.
Main Function
1
2
3
4
5
int main(void) {
  /* Declaration Statements */
  /* Executable  Statements */
  return 0;
}

Assignment Statements

Syntax

<lvalue> = <expr>

NOTE:

  • = means assign the result of evaluating the <expr> into the location described by the <lvalue>.
  • <lvalue> must be assignable.
  • The value being assigned is then returned as the result of the evaluation1.

The sequence of evaluation of an assignment is as follows:

  1. Evaluate <lvalue> into an assignable location.
  2. Evaluate <expr> into a value.
  3. Assign the value from (2) into the location from (1).

Memory

This sequence of operations allows us to evaluate an assignment that does not make sense mathematically: sum = sum + item. Mathematically, there is no such number that is equal to itself plus another number unless the other number is 0. As an assignment, the evaluation can be summarised in the diagram below.

Increment

Side Effect

One of the "note" on assignment is that

The value being assigned is then returned as the result of the evaluation.

This leads to the concept of cascaded assignment such as: a = b = c = 3 + 6;. In this case, the assignment proceed fro, right to left. The code is functionally equivalent to: a = (b = (c = 3 + 6));. More explicitly, it is:

1
2
3
c = 3 + 6;
b = c;
a = b;

However, there is a more sinister part of this: a = 5 + (b = 3);. In this case, we first assign the value 3 to b and then the entire expression is evaluated into 3. Next, we evaluate a = 5 + 3 as the result, which will assign the value 8 to a.

This return value often called the "side effect" of an assignment. It is useful to assign the same initial value to multiple variables. However, you should avoid writing convoluted codes such as the sinister example above.

Invalid Assignments

In an assignment, the <lvalue> must be an assignable location such as variable. If you try to run the code below, you will encounter a compilation error lvalue required as left operand of assignment.

1
2
32 = a;    // '32' is not an assignable location (e.g., variable)
a + b = c; // 'a + b' is an expression, also not assignable

Arithmetic Operations

The arithmetic operations in C can be summarised as the table below:

Arithmetic Operations

Code Behaviour
x + y Add y to x
x - y Subtract y from x
x * y Multiply x and y
x / y Divide x by y; if both x and y are int, then truncate, otherwise operate as double
x % y The remainder after x is divided by y
x++ Return the value of x; then increment x by 1
x-- Return the value of x; then decrement x by 1
++x Increment x by 1; then return the value of x
--x Decrement x by 1; then return the value of x

Increment/Decrement

Note the order of the increment/decrement and return of value. If the variable x has a value of 3, then after:

  • y = x++; \(\rightarrow\) y has a value of 3 and x has a value of 4.
  • y = x--; \(\rightarrow\) y has a value of 3 and x has a value of 2.
  • y = ++x; \(\rightarrow\) y has a value of 4 and x has a value of 4.
  • y = --x; \(\rightarrow\) y has a value of 2 and x has a value of 2.

When multiple operations are used in a single expression, the order of evaluation follows the operator precedence.

Operator Precedence

Precedence Kinds Operators Associativity Examples
1 Postfix Unary ++, -- Left to right x--, x++
2 Prefix Unary +, -, ++, --, &, *, (type), ! Right to left x = +4;, x = -23;, --x;
3 Multiplicative Binary *, /, % Left to right x * y, z % 2;
4 Additive Binary +, - Left to right x + y, z - 2;
5 Relational <=, >=, <, > Left to right x < y, z >= 2;
6 Equality ==, != Left to right x != y, z == 2;
7 Conjunction && Left to right x == y && x == 1
8 Disjunction || Left to right x == y || x == 1
9 Assignment =, +=, -=, *=, /=, %= Right to left x += y, z = 2;

The operators with the lower precedence are evaluated first. Since there can be multiple operators with the same precedence, the associativity is used to resolve the order.

Parentheses

If you are not sure about the precedence, parentheses can be used to force a certain ordering. As a good practice, you should write parentheses whenever you are in doubt.

One way to easily evaluate any expression is to put parentheses around the operands based on the precedence above. For instance, given the expression 5 - 6 * 7 + 4 / 5, we repeatedly put parentheses using the precedence table from lowest precedence to largest precedence. If there are operators on the same precedence level, we resolve by associativity.

Precedence Resolution and Evaluation

Precedence Resolution
1
2
3
4
5
5 - 6 * 7 + 8 / 5
 5 - (6 * 7) + 8 / 5       // */ higher precedence over +-; left-associative
 5 - (6 * 7) + (8 / 5)     // /  higher precedence over +-
 (5 - (6 * 7)) + (8 / 5)   // +- has the same precedence; left-associative
 ((5 - (6 * 7)) + (8 / 5)) // +  is the only operation left
Evaluation
1
2
3
4
5
((5 - (6 * 7)) + (8 / 5))
 ((5 - 42) + (8 / 5)) // innermost
 (-37 + (8 / 5))      // left-most
 (-37 + 1)            // innermost: integer division
 -36                  // simple operation

Evaluation Order

"Left-most innermost"

To show the arithmetic operation in action, we will use the program ArithOps.c below:

Arithmetic Operations

ArithOps.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// To illustrate some arithmetic operations in C
#include <stdio.h>
int main(void) {
  int x, p, n; 

  // to show left associativity
  printf("46 / 15 / 2 = %d\n", 46/15/2);
  printf("19 %% 7 %% 3 = %d\n", 19%7%3);

  // to show right associativity
  x = -23;
  p = +4 * 10;
  printf("x = %d\n", x);
  printf("p = %d\n", p);

  // to show truncation of value
  n = 9 * 0.5;
  printf("n = %d\n", n);

  return 0;
}

Division and Remainder

When we perform operations with different numeric types (i.e., combinations of int and either float or double), the result follow the most general type. Since technically all whole numbers can be represented as real numbers by adding .0 at the end, float and double are considered more general than int.

In the case of division where both operands are int, we also expect the result to be int. This resulting int is obtained by simply truncating the values after the decimal point. Such division operation is called integer division. Not to be confused with Python's floor division.

Division Operation

z x y z = x/y
int 10 4 z = 2
int 10 4.0 z = 2
int 10.0 4 z = 2
int 10.0 4.0 z = 2
double 10 4 z = 2.0
double 10 4.0 z = 2.5
double 10.0 4 z = 2.5
double 10.0 4.0 z = 2.5
x y C: x/y C: x/y Python: x//y Python: x%y
10 4 2 2 2 2
10 -4 -2 2 -3 -2
-10 4 -2 -2 -3 2
-10 -4 2 -2 2 -2

Note: Python // division is a floor division. The result is the integer smaller than or equal to the result.

Type casting may also affect the division operation. But since type cast operation has a very high precedence, we have to be careful with the order of operation.

Type Cast

TypeCast.c
1
2
3
4
5
6
7
8
int main(void) {
  int aa = 6; float ff = 15.8;
  float pp = (float) aa / 4;   // pp = 6.0 / 4 = 1.5
  int   nn = (int) ff / aa;    // nn = 15 / 6 = 2
  float qq = (float) (aa / 4); // qq = (float) (6/4) = (float) 1 = 1.0
  printf("%f %d %f\n", pp, nn, qq);
  return 0;
}

Selection

C provides two control structures that allow you to select a group of statements to be executed or skipped when certain conditions are met. The conditions are expressions that are evaluated to Boolean values (i.e., either true or false2). They are usually composed of expressions combined with relational operators and logical operators.

Boolean

You may have noticed that both true and false are not part of reserved keywords. This is because they are indeed not. To actually use these keywords, you will need to include the Boolean library using #include <stdbool.h>. The type of Boolean values in C is called bool. If you do not wish to include the Boolean library, you may use the following macro expansion:

Boolean
1
2
3
#define false 0
#define true  1
#define bool  char

As the Boolean macro expansion above shows, true is equal to 1 and false is equal to 0. However, any non-zero values are treated as true. But note, non-zero values except 1 is not equal to true. This will be made clear below.

Arithmetic Operations

Relational Interpretation
< Less than
<= Less than or equal to
> Greater than
>= Greater than or equal to
== Equal to
!= Not equal to
Comparison with JavaScript and Python

Unlike JavaScript, there is no such thing as "strict" equality (i.e., ===) or inequality (i.e., !==). Also, if you are coming from Python, you might find that 1 <= x <= 5 has a wildly different meaning than in Python. In C, relational operators are treated like any other operators so the expression above is equivalent to (1 <= x) <= 5. In which case, you either get true <= 5 or false <= 5 depending on the value of x.

Logical operators are used to combine two or more Boolean expressions. They can be used to form complex logical statements such as:

  • If temperature is greater than 40C or blood pressure is greater than 200, then go to A&E immediately.
  • If all the three subject scores (English, Maths and Science) are greater than 85 and mother tongue score is at least 80, recommend taking Higher Mother Tongue.

We usually summarise the logical operators using their truth table.

Truth Table

X Y X && Y X || Y !X
true true true true false
true false false true false
false true false true true
false false false false true
X X && Y X || Y !X
true Y true false
false false Y true

Short-Circuit

The main difference between short-circuit truth table and the basic truth table is whether the variable/expression represented by Y is going to be evaluated or not. In the case of &&, Y is only evaluated when X is true and in the case of ||, Y is evaluated only when X is false.

This allows for a "safe" execution especially in the case where the evaluation of Y may result in an error. Consider the case of finding an inverse of a number (i.e., 1/x). x cannot be 0 because we cannot divide by zero. We can safeguard this using short-circuit evaluation:

Short-Circuit

Safe Inverse
1
2
3
if((a != 0) && (b/a > 3)) {
  printf(...);
}

It is important to note that the result of relational and logical operators are always Boolean. In other words, it will always be either true or false.

Non-Zero

We mentioned that any non-zero values are treated as true but only 1 is equal to true. What do we mean by that? We will illustrate this with examples instead.

The first example below shows how we can evaluate a logical operation above using non-zero values.

Non-Zero as True

NonZeroTrue.c
1
2
printf("%d\n", 5 && 0); // 5 is true and 0 is false: true && false == false (0)
printf("%d\n", -1 || 0); // -1 is true and 0 is false: true || false == true  (1)

To evaluate both lines, we first find the corresponding Boolean values:

  • 5: true
  • 0: false
  • -1: true

We then convert the expressions using the Boolean:

  • Line 1: true && falsefalse
  • Line 2: true || falsetrue

As we print this as integer %d, we convert the Boolean back to integer:

  • false: 0
  • true: 1

The second example below shows how these non-zero values --although treated as true-- are not equal to true unless the value is 1. While we did not mention false, it should be clear that is will be equal to 0.

True is One

TrueIsOne.c
1
2
3
4
5
printf("%d\n", 1 == true);   // true  (1)
printf("%d\n", -1 == true);  // false (0): although -1 is treated as true
printf("%d\n", 0 == true);   // false (0)
printf("%d\n", 0 == false);  // true  (1)
printf("%d\n", -1 || false); // true  (1)

The main part here is line 2 and line 5. At line 5, we show that -1 is treated as true because otherwise the output will not be true or 1. However, we cannot equate -1 with true using -1 == true. This will instead produce false.

Quick Quiz

What are the values of x, y and z?

1
2
3
4
5
int x, y, z
    a = 4, b = -2, c = 0;
x = (a > b || b > c && a == b);
y = ((a > b || b > c) && a == b);
z = ((a > b) && !(b > c));

  • x is true but there is a warning from the compiler. This warning is because && has higher precedence than || so the warning is for programmers who may not know this precedence.
  • y is true false and it is always good to add parentheses for readability. Note, it should be false, the previous version of this is wrong as it put true instead. The run on ReplIt should be taken as a base answer in case of discrepancy.
  • z is true.

Selection Statement

If-Statement

If-Statement

If
1
2
3
if(condition) {
  /* Execute these statements if TRUE  */
}
If-Else
1
2
3
4
5
if(condition) {
  /* Execute these statements if TRUE  */
} else {
  /* Execute these statements if FALSE */
}

Unlike Python, there is no elif. The magic here is that the curly bracket is optional if there is only one statement within the block. Since an if is technically a single statement, there is no need for curly bracket between else and if.

Else-If
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
if(cond1) {
  /*
    Execute these statements if cond1 is TRUE
  */
  then_body;
} else if(cond2) {
  /*
    Execute these statements if cond1 is FALSE
    and cond2 is TRUE
  */
  elif_body;
} else {
  /*
    Execute these statements if cond1 is FALSE
    and cond2 is FALSE
  */
  else_body;
}

This flowchart uses the code from "Elif" tab.

If

Absolute Value

The absolute value of a number n, written as |n|, is the magnitude of the number. In other words, it is how far the number is from 0 in the number line. This value is always positive. For instance, the magnitude of 5, denoted by |5|, is simply 5. On the other hand, the magnitude of -10, denoted by |-10| is simply 10.

Write a C program to read a single integer input and print the absolute value of the number.

The idea is to check if the number is negative. If it is, we then convert it to a positive value by multiplying it with -1.

Answer
1
2
3
4
5
6
int number;
scanf("%d", &number);
if(number < 0) {
  number = number * -1;
}
printf("%d\n", number);

Switch-Case

Switch-Case

Switch
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
switch(variable_or_expression) {
  case value1:
    /* Code to execute if variable_or_expression == value1 */
    value1_body;
    break; // Prevents spill over to next case
  case value2:
    /* Code to execute if variable_or_expression == value2 */
    value2_body;
    // no break can spill over to next case
  case value3:
    /* Code to execute if variable_or_expression == value3 */
    value3_body;
    break;
  default:
    /*
      Code to execute if variable_or_expression
      is not equal to any of the cases above
    */
    default_body;
}
  • Variable or expression in variable_or_expression must be of discrete type (_usually char or int).
  • value1, value2, value3, ... need not be in any particular order.
  • There can be 0 or more case.
  • There can be 0 or 1 default.
  • If there is default, it must be after all the case.

This flowchart uses the code from "Syntax" tab.

Switch

Chess

With the rise in popularity the Netflix series Queen's Gambit, you decide to write a program that can partially read a chess notation. In this case, you are simply going to read a single character and print the corresponding chess piece according to the following table. The symbol is provided simply to show you what the piece looks like.

Character Piece Symbol
'K' King
'Q' Queen
'R' Rook
'B' Bishop
'N' Knight

How do you read a single character? The placeholder is %c. Also, don't forget the break; to avoid spill over to the next case.

Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
char character;
scanf("%c", &character);
switch(character) {
  case 'K':
    printf("King\n");
    break;
  case 'Q':
    printf("Queen\n");
    break;
  case 'R':
    printf("Rook\n");
    break;
  case 'B':
    printf("Bishop\n");
    break;
  case 'N':
    printf("Knight\n");
    break;
}

Repetition

Repetition Statement

C provides three control structures that allow you to select a group of statements to be executed repeatedly.

While-Loop

While-Loop

While-Loop
1
2
3
while(condition) {
  body;
}

This flowchart uses the code from "Syntax" tab.

Switch

Sum to 10

Consider a code to sum a number from 1 to 10.

Sum1To10_While.c
1
2
3
4
5
int sum = 0, i = 1;
while(i <= 10) {
  sum = sum + i;
  i++;
}

Do-While-Loop

Do-While-Loop

Do-While-Loop
1
2
3
do {
  body;
} while(condition);

This flowchart uses the code from "Syntax" tab.

Switch

Sum to 10

Consider a code to sum a number from 1 to 10.

Sum1To10_DoWhile.c
1
2
3
4
5
int sum = 0, i = 1;
do {
  sum = sum + i;
  i++;
} while(i <= 10);

While vs Do-While vs For

While vs Do-While Do while loop always perform the body at least once. So the difference might be subtle and only on the corner cases. This is best illustrated by the image on the left.

Additionally, as you may have guessed, a for-loop can be converted to a while-loop simply by

  • moving init to before the for-loop, and
  • moving update to after body inside the loop

Although you are mostly correct, the behaviour can differ in a subtle way when we take into account break and continue.

For-Loop

For-Loop

For-Loop
1
2
3
for(init; condition; update) {
  body;
}

This flowchart uses the code from "Syntax" tab.

Switch

Sum to 10

Consider a code to sum a number from 1 to 10.

Sum1To10_For.c
1
2
3
4
int sum, i;
for(sum = 0, i = 1; i  <= 10; i++) {
  sum = sum + i;
}

Break and Continue

There are two statements that can be used within a repetition structures:

  1. break: This statement exits the nearest loop. In other words, it jumps out of the nearest loop3.
  2. continue: This statement continues to the end of the loop body. In other words, it jumps to just after the last line of the body of the nearest loop but before going back to the top.

Break, Continue, For

As you will see in the examples below, there is a difference in the behaviour of continue between for-loop and while-loop (or do-while-loop). The difference is subtle and it involves the update component of the for-loop.

When a continue is executed, we do jump to the end of the loop body. However, the update is performed after the loop body. As such, the update is still going to be executed and not skipped when using continue.

On the other hand, if we do a rewriting as follows:

1
2
3
for(init; condition; update) {
  body;
}
1
2
3
4
5
init;
while(condition) {
  body;
  update;
}

If there is a continue in the body, the for-loop version will still execute the update but the while-loop version will not execute the update.

Break

BreakInLoop.c
1
2
3
4
5
printf("Without 'break':\n");
for(i=1; i<=5; i++) {
  printf("%d\n", i);
  printf("Ya\n");
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Without 'break':
1
Ya
2
Ya
3
Ya
4
Ya
5
Ya
BreakInLoop.c
1
2
3
4
5
6
7
printf("With 'break':\n");
for(i=1; i<=5; i++) {
  printf("%d\n", i);
  if(i == 3)
    break;
  printf("Ya\n");
}
BreakInLoop.c
1
2
3
4
5
6
7
printf("With 'break':\n");
for(i=1; i<=5; i++) {
  printf("%d\n", i);
  if(i == 3)
    break; // ----------+
  printf("Ya\n");  //   |
} // <------------------+
1
2
3
4
5
6
With 'break':
1
Ya
2
Ya
3
BreakInLoop.c
1
2
3
4
5
6
7
8
9
printf("With 'break' in a nested loop:\n");
for(i=1; i<=3; i++) {
  for(j=1; j<=5; j++) {
    printf("%d, %d\n", i, j);
    if(j == 3)
      break;
    printf("Ya\n");
  }
}
BreakInLoop.c
1
2
3
4
5
6
7
8
9
printf("With 'break' in a nested loop:\n");
for(i=1; i<=3; i++) {
  for(j=1; j<=5; j++) {
    printf("%d, %d\n", i, j);
    if(j == 3)
      break; // ---------+
    printf("Ya\n"); //   |
  } // <-----------------+
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
With 'break' in a nested loop:
1, 1
Ya
1, 2
Ya
1, 3
2, 1
Ya
2, 2
Ya
2, 3
3, 1
Ya
3, 2
Ya
3, 3

Continue

ContinueInLoop.c
1
2
3
4
5
printf("Without 'continue':\n");
for(i=1; i<=5; i++) {
  printf("%d\n", i);
  printf("Ya\n");
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Without 'continue':
1
Ya
2
Ya
3
Ya
4
Ya
5
Ya
ContinueInLoop.c
1
2
3
4
5
6
7
printf("With 'continue':\n");
for(i=1; i<=5; i++) {
  printf("%d\n", i);
  if(i == 3)
    continue;
  printf("Ya\n");
}
ContinueInLoop.c
1
2
3
4
5
6
7
printf("With 'continue':\n");
for(i=1; i<=5; i++) { // <--+
  printf("%d\n", i);  //    |
  if(i == 3)  //            |
    continue; // -----------+
  printf("Ya\n");
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
With 'continue':
1
Ya
2
Ya
3
4
Ya
5
Ya
ContinueInLoop.c
1
2
3
4
5
6
7
8
9
printf("With 'continue' in a nested loop:\n");
for(i=1; i<=3; i++) {
  for(j=1; j<=5; j++) {
    printf("%d, %d\n", i, j);
    if(j == 3)
      continue;
    printf("Ya\n");
  }
}
ContinueInLoop.c
1
2
3
4
5
6
7
8
9
printf("With 'continue' in a nested loop:\n");
for(i=1; i<=3; i++) {
  for(j=1; j<=5; j++) { // <-------+
    printf("%d, %d\n", i, j); //   |
    if(j == 3)  //                 |
      continue; // ----------------+
    printf("Ya\n");
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
With 'continue' in a nested loop:
1, 1
Ya
1, 2
Ya
1, 3
1, 4
Ya
1, 5
Ya
2, 1
Ya
2, 2
Ya
2, 3
2, 4
Ya
2, 5
Ya
3, 1
Ya
3, 2
Ya
3, 3
3, 4
Ya
3, 5
Ya

Exercises

Arithmetic

Celcius to Fahrenheit

We can convert a temperature given in Celcius to Fahrenheit using the following formula:

T°F = T°C × 9 ÷ 5 + 32

The operation precedence is the normal precedence. Write a program to read the temperature given in Celcius as integer and print the corresponding temperature in Fahrenheit printed to two decimal places.

Sample Run 1

Input
1
0
Output
1
32.00

Sample Run 2

Input
1
100
Output
1
212.00

Sample Run 3

Input
1
32
Output
1
89.60

Did you ensure that you are not doing integer division? How would you prevent doing integer division?

There are many ways, the two common ways are:

  1. (double) x/y
  2. x*1.0/y

Note that x/y*1.0 does NOT work because we perform x/y first which already gives us integer before changing it to double.

How do you print 2 decimal places?

The placeholder is %.2f.

Answer
1
2
3
4
5
int celcius;
double fahrenheit;
scanf("%d", &celcius);
fahrenheit = celcius * 9.0 / 5 + 32;
printf("%.2f\n", fahrenheit);

Note at line 4 (highlighted above), we multiply by 9.0 to ensure that we are not performing integer division.

Fahrenheit to Celcius

We can convert a temperature given in Fahrenheit to Celcius using the following formula:

T°C = (T°F - 32) × 5 ÷ 9

The operation precedence is the normal precedence. Write a program to read the temperature given in Fahrenheit as integer and print the corresponding temperature in Celcius printed to two decimal places.

Sample Run 1

Input
1
32
Output
1
0.00

Sample Run 2

Input
1
212
Output
1
100.00

Sample Run 3

Input
1
100
Output
1
37.78

Did you ensure that you are not doing integer division? How would you prevent doing integer division?

There are many ways, the two common ways are:

  1. (double) x/y
  2. x*1.0/y

Note that x/y*1.0 does NOT work because we perform x/y first which already gives us integer before changing it to double.

How do you print 2 decimal places?

The placeholder is %.2f.

Did you ensure the correct order of operation?

The subtraction should be performed before multiplication. So, you need parentheses (x - 32) * y. Without the parentheses (i.e., x - 32 * y), you will be performing multiplication before subtraction.

Answer
1
2
3
4
5
int fahrenheit;
double celcius;
scanf("%d", &fahrenheit);
celcius = (fahrenheit - 32) * 5.0 / 9;
printf("%.2f\n", celcius);

We put parentheses around the subtraction to ensure that it is performed first.

Selection Statement

Leap Year

According to NASA

What is a Leap Year?

It takes approximately 365.25 days for Earth to orbit the Sun — a solar year. We usually round the days in a calendar year to 365. To make up for the missing partial day, we add one day to our calendar approximately every four years. That is a leap year.

A leap year can be checked by doing arithmetic operation on the year as follows:

  • If the year is divisible by 4, then it is a leap year.
  • EXCEPT:
    • If the year is also divisible by 100, then it is common year.
    • EXCEPT:
      • If the year is also divisible by 400, then it is a common year.
  • Otherwise it is a common year.

Write a C program to read a single integer input and print "Common Year" if the year is a common year and print "Leap Year" if the year is a leap year.

Sample Run 1

Input
1
2004
Output
1
Leap Year

Divisible by 4 and it is a leap year.

Sample Run 2

Input
1
2100
Output
1
Common Year

Although it is divisible by 4, it is also divisible by 100. So it is a common year.

Sample Run 3

Input
1
2000
Output
1
Leap Year

Although it is divisible by 4 and divisible by 100, it is also divisible by 400. So it is a leap year.

How do you check if a number x is divisible by another number y?

x % y == 0

Which check is the most relevant to check for leap year?

Checking divisibility by 400 first because this number will definitely be disivible by 100 and 4. If so, then definitely a leap year.

Then the second most relevant is divisibility by 100 because such number will also be divisible by 4 (since 4 × 25 = 100). If so, then definitely a common year assuming we have ruled out divisibility by 400.

Lastly, we simply check divisibility by 4 to determine the remaining possibilities.

Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int year;
scanf("%d", &year);
if(year % 400 == 0) {
  printf("Leap Year\n");
} else if(year % 100 == 0) {
  printf("Common Year\n");
} else if(year % 4 == 0) {
  printf("Leap Year\n");
} else {
  printf("Common Year\n");
}
BMI

According to the CDC, the formula for computing the BMI is as follows:

BMI Formula

weight ÷ (height)2

The weight is given in kg and the height is given in m. Once you have computed the BMI, we can check the categories:

  • Underweight if the BMI is less than 18.5.
  • Normal if the BMI is greater than or equal to 18.5 but less than 25.0.
  • Overweight if the BMI is greater than or equal to 25.0 but less than 30.0.
  • Obese if the BMI is greater than or equal to 30.0.

Write a program that accepts two inputs: weight in kg and height in m. Both inputs are real numbers and given in a single line. The program should output the category.

Sample Run 1

Input
1
60 1.70
Output
1
Normal

The BMI is 20.8.

Sample Run 2

Input
1
80 1.75
Output
1
Overweight

The BMI is 26.1.

Sample Run 3

Input
1
80 1.60
Output
1
Obese

The BMI is 31.2.

How do you perform squaring?

Simply height * height. But you need to do this first before dividing weight by this. Alternatively, divide twice:

weight / height / height.

Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
double weight, height, bmi;
scanf("%lf %lf", &weight, &height);
bmi = weight / height / height;
if(bmi < 18.5) {
  printf("Underweight\n");
} else if(bmi < 25.0) {
  printf("Normal\n");
} else if(bmi < 30.0) {
  printf("Overweight\n");
} else {
  printf("Obese\n");
}

Repetition Statement

3n+1

This problem is also called the Collatz Conjecture. We start with a simple computation given an input n:

  • If n is even, the next value of n is n ÷ 2.
  • If n is odd, the next value of n is 3 × n + 1.

The conjecture states that starting from any positive n (i.e., n ≥ 1), the value of n will eventually reach 1. This has been checked for very large numbers but has never been proven. What we want to know is how many steps until we reach 1.

Write a program that reads a single integer and print the number of steps until the number reaches 1. You are guaranteed that the input will be at least 1.

Sample Run 1

Input
1
3
Output
1
7

3 ⇒ 10 ⇒ 5 ⇒ 16 ⇒ 8 ⇒ 4 ⇒ 2 ⇒ 1

Sample Run 2

Input
1
7
Output
1
16

Sample Run 3

Input
1
1
Output
1
0

Already 1.

What kind of loop do you need?

While-loop. Unfortunately, because the input will be at least 1, using do-while-loop will perform the operation at least once which we should not do at all. You can also do for-loop by ignoring initialisation and update.

Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int n, step = 0;
scanf("%d", &n);
while(n > 1) {
  step++;
  if(n % 2 == 0) {
    n = n/2;
  } else {
    n = 3*n+1;
  }
}
printf("%d\n", step);
Average

Now for something slightly different. This problem is more of an input problem. The task is rather simple, we want to find the average of some numbers. The question is, how many numbers?

Well, that is also depend on the input. The input will consists of two parts:

  • The first line is a single integer n ≥ 1.
  • The next n lines are all real numbers.
    • You have to find the average of these numbers.
    • Print the average up to 2 decimal places.

Notice how we have not learnt about array yet. So, you cannot use them. Can you write a program to do this?

Sample Run 1

Input
1
2
3
4
5
6
5
1.1
1.2
1.3
1.4
1.5
Output
1
1.30

(1.1 + 1.2 + 1.3 + 1.4 + 1.5) ÷ 2 = 1.3

Without array, how do we store all these numbers?

We do not have to. The average is simply the total divided by how many numbers we have. So, we simply have to calculate the total as we read the number.

You are guaranteed that there will be at least two lines in the input.

Answer
1
2
3
4
5
6
7
8
int n, i;
double total = 0, curr;
scanf("%d", &n);
for(i=0; i<n; i++) {
  scanf("%lf", &curr);
  total += curr;
}
printf("%.2f\n", total/n);
Harder Average

Now this is similar to the average problem. But to make this harder, we do not know how many numbers are there! Instead, we will read the number until we encounter any negative number. We then compute the average excluding this negative number.

You are guaranteed that there will be at least two lines on the input. Additionally, the input will not be zero.

Sample Run 1

Input
1
2
3
4
5
6
1.1
1.2
1.3
1.4
1.5
-0.1
Output
1
1.30

(1.1 + 1.2 + 1.3 + 1.4 + 1.5) ÷ 2 = 1.3

How do we even read all these numbers?

Similar to before, we accumulate all values into the total. But we will not stop the iteration until we have read a negative number.

What kind of loop do you need?

Do-while-loop. This is the simplest because we have to read at least one number.

Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int n = 0, i;
double total = 0, curr;
do {
  scanf("%lf", &curr);
  total += curr;
  n++;
} while(curr > 0);
total -= curr; // offset the last number
n--;           // offset the last count
printf("%.2f\n", total/n);

Break and Continue

Efficient Prime Number

A prime number is a number that is divisible only by 1 and itself. Write a program to read in a number and print "prime" if the number is a prime number and print "composite" if the number is not a prime number. Can you do it efficiently?

Sample Run 1

Input
1
9
Output
1
composite

9 = 3 × 3

Sample Run 2

Input
1
5
Output
1
prime

When can you stop? How to stop?

We can simply stop when we have found a counter-example. In this case, when we find that the number is a composite number. We stop by using break;.

How many checks do you need?

It may seem like you need to check n-2 times (from 2 to n-1). But you simply have to check √n times. The proof for this is left as an exercise as this is not the main topic for this exercise.

Answer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int n, div;
bool is_prime = true; // a flag to indicate that the number is prime
scanf("%d", &n);
for(div=2; div*div <= n; div++) {
  // div*div <= n is the second improvement
  // but can simply do div < n
  if(n % div == 0) {
    is_prime = false;
    break;
  }
}
if(is_prime == true) {
  printf("prime\n");
} else {
  printf("composite\n");
}

Challenges

We will not give the answers for these challenges.

Normalised Average

Instead of a standard average, we will be computing the normalised average. Consider a sequence of numbers X1, X2, ..., Xn. We denote two important values:

  • Xmax = max(X1, X2, ..., Xn)
  • Xmin = min(X1, X2, ..., Xn)

For each number Xi, we scale this number by:

X'i = (Xi - Xmin) ÷ (Xmax - Xmin)

What we are computing is then the average of X'1, X'2, ..., X'n. The input is similar to the harder average problem. In other words, you do not know how many numbers are there. So, you cannot use array. Can you solve this?

For simplicity, you may assume that the number (except the negative one) will be between 1 and 1000 (inclusive).

Sample Run 1

Input
1
2
3
4
5
6
1
2
3
4
5
-1
Output
1
0.50

Sample Run 2

Input
1
2
3
4
5
6
1.1
1.2
1.3
1.4
1.5
-1.0
Output
1
0.50

Sample Run 2

Input
1
2
3
4
5
6
1.1
1.1
1.1
1.2
1.3
-1.0
Output
1
0.30


  1. Depending on the source you read, this return value of an assignment is either the main effect or the side effect. In most literature, a side effect is the effect on the memory due to the functional abstraction. As a function, we are only interested in the mapping between the input and the output. Any other behaviours (e.g., changes in memory), are considered side effect. 

  2. If you are from Python, note that the first letter is in lowercase and not uppercase. 

  3. Nearest is in terms of nesting. We either exit to the end or continue back to top of the inner-most loop that contains the break/continue