COMP10110 Computer Programming I - Lecture 5: Conditionals and Boolean Expressions
COMP10110 Computer Programming I - Lecture 5: Conditionals and Boolean Expressions
This lecture covers the fundamental concepts of conditional statements in C programming, including if, if...else, and nested if...else constructs. It delves into boolean expressions, equality and relational operators, logical operators, the nuances of floating-point comparisons, and operator precedence. Special attention is given to C's unique handling of boolean values as integers and the introduction of the bool type in C11.
Conditionals: The if Statement
Concept of Decision Making
We make decisions in daily life based on conditions (e.g., "If I get hungry, I will eat my lunch").
These are called conditional sentences, comprising two parts:
Condition Part: The
ifclause (e.g., "If I get hungry").Action Part: The consequence (e.g., "I will eat my lunch").
The action is executed only if the condition is met.
Testing Conditions
A condition can be rephrased as a question with a yes/no answer (e.g., "Am I hungry?").
A "yes" answer means the condition is true (or evaluates to true).
A "no" answer means the condition is false (or evaluates to false).
The action is performed exclusively when the condition is true.
Conditionals in C
The C programming language provides conditional statements to implement this concept.
The keyword
ifis used to define a conditional statement.
Structure of
ifStatementif (condition) { // Compound statement (action) to be executed if condition is true }The
conditionis a boolean expression enclosed in round brackets(). A boolean expression is one that evaluates to either true or false.The
compound statementis a set of statements contained within a pair of braces{ }. This block is executed only if theconditionevaluates totrue; it is skipped if theconditionevaluates tofalse.
Example:
ifStatement/* Program to show the use of the if statement. */ #include <stdio.h> #define PASS_MARK 40 int main(void) { int mark; printf("Enter a mark (as an integer):\n"); scanf("%d", &mark);if (mark >= PASS_MARK) { // Condition: mark greater than or equal to 40 printf("A pass mark.\n"); } return 0;}In this example,
mark >= PASS_MARKis the boolean expression. If the enteredmarkis 40 or higher, the message "A pass mark." will be printed.
Common Error: Semicolon after
ifConditionPlacing a semicolon
;immediately after theifcondition is a common mistake that changes the program's logic.Incorrect Example:
c if (mark >= PASS_MARK); /* error */ { printf("A pass mark.\n"); }Effects of this error:
The
ifstatement's body is considered empty. Theifstatement does nothing, even if its condition is true.The statement
printf("A pass mark.\n");is treated as a separate, independent statement and will always be executed, regardless of whethermark >= PASS_MARKevaluates totrueorfalse.
Conditionals: The if...else Statement
Purpose
The
if...elsestatement allows specifying different actions fortrueandfalseconditions.
Structure
if (condition) { // Compound statement 1 (executed if condition is true) } else { // Compound statement 2 (executed if condition is false) }If
conditionevaluates totrue, onlyCompound statement 1(afterif) is executed.If
conditionevaluates tofalse, onlyCompound statement 2(afterelse) is executed.
Example:
if...elseStatement/* Program to show the use of the if...else statement. */ #include <stdio.h> #define PASS_MARK 40 int main(void) { int mark; printf("Enter a mark (as an integer):\n"); scanf("%d", &mark);if (mark >= PASS_MARK) { printf("A pass mark.\n"); } else { printf("Not a pass mark.\n"); } return 0;}
Conditionals: Nested if...else Statements
Purpose
Used to evaluate multiple conditions by placing
if...elsestatements inside otherif...elsestatements.
Equivalency
C compilers treat different nesting arrangements as equivalent if their logic produces the same outcome.
The lecture provides two examples (Example 1 and Example 2) that are functionally equivalent.
Example 1: Traditional Nested
if...else#include <stdio.h> #define PASS_MARK 40 #define DISTINCTION_MARK 70 int main(void) { int mark; printf("Enter a mark (as an integer):\n"); scanf("%d", &mark);if (mark >= DISTINCTION_MARK) { printf("Distinction\n"); } else { // If not distinction, check for pass or fail if (mark >= PASS_MARK) { printf("Pass\n"); } else { printf("Fail\n"); } } return 0;}Example 2:
else ifLadder (More Compact)#include <stdio.h> #define PASS_MARK 40 #define DISTINCTION_MARK 70 int main(void) { int mark; printf("Enter a mark (as an integer):\n"); scanf("%d", &mark);if (mark >= DISTINCTION_MARK) { printf("Distinction\n"); } else if (mark >= PASS_MARK) { // Only checked if first condition is false printf("Pass\n"); } else { // Only reached if both previous conditions are false printf("Fail\n"); } return 0;}
Equality and Relational Operators
Comparison of Numbers
Six primary ways to compare two numbers, x and y:
Equal: is x equal to y? (C operator:
x == y)Not Equal: is x not equal to y? (C operator:
x != y)Greater Than: is x greater than y? (C operator:
x > y)Less Than: is x less than y? (C operator:
x < y)Greater Than or Equal: is x greater than or equal to y? (C operator:
x >= y)Less Than or Equal: is x less than or equal to y? (C operator:
x <= y)
Categories of Operators
Equality Operators:
==and!=Relational Operators:
>,<,>=,<=
Evaluation Result
Each of these operators evaluates to either
trueorfalse.
Important Note: Assignment vs. Equality
Take extreme care not to confuse the equality operator (
==) with the assignment operator (=).x == ychecks ifxis equal toy.x = yassigns the value ofytox.
Equality Operators – Floating-Point Numbers
The Issue with Floating-Points
Care is required when using equality operators (
==,!=) with floating-point numbers (e.g.,float,double).Integers are stored exactly, so this issue doesn't arise with
inttypes.Floating-point numbers can have precision issues due to their internal binary representation, meaning that what appears mathematically equal might not be exactly equal in computer memory.
Example of Floating-Point Precision Error
double x = 0.1; double y = 0.3 - 0.1 - 0.1; // Mathematically y should be 0.1 double diff = x - y; printf("diff is %le\n", diff); /* Output: diff is 2.775558e-17 */In this case,
x == ywould evaluate tofalsebecauseyis not exactly 0.1 due to internal precision. Thediffshows a very small, non-zero difference.
Solution: Comparing with a Tolerance Value (EPS)
To robustly compare two floating-point variables, x and y, for equality, check if the absolute difference between them is less than a small tolerance value (often called
EPSor epsilon).This approach accounts for minor floating-point inaccuracies.
Formula:
fabs(x - y) < EPSfabs()function (from<math.h>in C) calculates the absolute value for floating-point numbers.EPSshould be a very small positive number, e.g., 1e-10 or 1e-12.
Example using Tolerance
#include <stdio.h> #include <math.h> // For fabs // Define a small tolerance value #define EPS 1e-10 int main(void) { double x = 0.1; double y = 0.3 - 0.1 - 0.1;if (fabs(x - y) < EPS) { printf("equal\n"); /* Output: equal is printed */ } else { printf("not equal\n"); } return 0;}
Logical Operators
Purpose
Used to combine multiple boolean conditions to form more complex boolean expressions.
Categories
Binary Logical Operators: Operate on two boolean values.
AND
OR
XOR (Exclusive OR)
Unary Logical Operator: Operates on a single boolean value.
NOT
C Symbols for Logical Operators
&&: Logical AND||: Logical OR!: Logical NOT
Truth Tables
AND Operator (
a && b)Evaluates to
trueonly if bothaistrueANDbistrue.aba AND bfalse
false
false
false
true
false
true
false
false
true
true
true
OR Operator (
a || b)
Evaluates to
trueifaistrueORbistrueOR both aretrue.
aba OR b:----
:----
:-------
false
false
false
false
true
true
true
false
true
true
true
true
XOR Operator (Exclusive OR)
Evaluates to
trueif either (aistrueANDbisfalse) OR (aisfalseANDbistrue). It isfalseif both are the same.
Note: There is no direct
XORoperator symbol in C. It can be implemented using other logical operators (e.g.,(a || b) && !(a && b)ora != b).
aba XOR b:----
:----
:--------
false
false
false
false
true
true
true
false
true
true
true
false
NOT Operator (
!a)
Inverts the boolean value of
a.
aNOT a:----
:------
false
true
true
false
Precedence of Logical Operators (Specific Hierarchy)
When multiple logical and comparison operators are used in an expression, their evaluation order is determined by precedence rules.
Logical NOT (
!): Highest precedence among logical operators.
Size Comparators:
<,>,<=,>=.
Equality Comparators:
==,!=.
Logical AND (
&&).
Logical OR (
||): Lowest precedence among logical operators.
For binary operators of equal precedence, evaluation typically occurs from left to right.
Recommendation: Use parentheses
()to explicitly define the order of evaluation, especially for complex expressions, to improve readability and prevent unexpected results.
Lazy Evaluation (Short-Circuiting) of Logical Operators
Concept
C utilizes lazy evaluation (also known as short-circuiting) for the binary logical operators
&&(AND) and||(OR).Arguments are evaluated strictly from left to right.
If the outcome of the entire expression can be determined solely by evaluating the left argument, the right argument is not evaluated at all.
How it works
For
&&(AND):If the left argument evaluates to
false, the entire&&expression must befalse(sincefalse && anythingis alwaysfalse). The right argument is skipped.
For
||(OR):If the left argument evaluates to
true, the entire||expression must betrue(sincetrue || anythingis alwaystrue). The right argument is skipped.
Examples of Lazy Evaluation (Assume a = 0 and b = 1)
(a > 0) && (b < 2)(a > 0)is(0 > 0), which isfalse.Since the left argument is
false, the&&operator immediately returnsfalse. The right argument(b < 2)is not evaluated.
(a >= 0) && (b < 2)(a >= 0)is(0 >= 0), which istrue.Since the left argument is
true, the&&operator needs to evaluate the right argument.(b < 2)is(1 < 2), which istrue.Both are
true, so&&returnstrue.
(a > 0) || (b < 2)(a > 0)is(0 > 0), which isfalse.Since the left argument is
false, the||operator needs to evaluate the right argument.(b < 2)is(1 < 2), which istrue.One is
true, so||returnstrue.
(a >= 0) || (b < 2)(a >= 0)is(0 >= 0), which istrue.Since the left argument is
true, the||operator immediately returnstrue. The right argument(b < 2)is not evaluated.
Example: Logical Operators in C
Program to Check Divisibility
/* Program to check if an integer is: * (i) divisible by 2 or by 3 * (ii) divisible by 2 and by 3 */ #include <stdio.h> int main(void) { int n; printf("Enter an integer:\n"); scanf("%d", &n);/* (i) check if n is divisible by 2 or by 3 */ if ((n % 2 == 0) || (n % 3 == 0)) { printf("divisible by 2 or by 3\n"); } else { printf("not divisible by 2 or by 3\n"); } /* (ii) check if n is divisible by 2 and by 3 */ if ((n % 2 == 0) && (n % 3 == 0)) { printf("divisible by 2 and by 3\n"); } else { printf("not divisible by 2 and by 3\n"); } return 0;}
Operator Precedence (Comprehensive)
General Rules
Operators with higher precedence are evaluated before operators with lower precedence.
Operators with equal precedence are grouped together, and their evaluation order (associativity) is typically left-to-right, except for some specific cases (like unary operators and assignment).
Table of Operators (Descending Precedence)
C Operator
Meaning
Associativity
+unary plus
right to left
-unary minus
right to left
!unary logical NOT
right to left
*multiplication
left to right
/division
left to right
%modulo
left to right
+addition
left to right
-subtraction
left to right
<less than
left to right
<=less than or equal to
left to right
>greater than
left to right
>=greater than or equal to
left to right
==equal to
left to right
!=not equal to
left to right
&&logical AND
left to right
||logical OR
left to right
=assignment
right to left
The
boolType and Boolean Expressions in CTraditional C (Prior to C11)
In earlier C standards, there was no dedicated boolean type.
Boolean expressions were evaluated as integers.
0(zero) was defined asfalse.Any non-zero value was defined as
true.Thus, conditions could be:
A boolean expression that explicitly evaluates to
0(false) or1(true).Any expression that evaluates to
0(false) or anon-zerovalue (true).
Examples of C's Integer Boolean Logic (Traditional C)
int x = 0; int y = 1; int z = 2; if (x) /* Condition (0) evaluates to false */ { printf("This will not be executed.\n"); } if (!x) /* Condition (!0, which is 1) evaluates to true */ { printf("This will be executed.\n"); } if (y - z) /* Condition (1 - 2 = -1) evaluates to true (non-zero) */ { printf("This will be executed.\n"); }c int b; /* an integer used as a boolean */ b = (1 > 2); /* (1 > 2) evaluates to false (0), b is assigned 0 */ if (b) /* Condition (0) evaluates to false */ { printf("This will not be executed.\n"); }c int b; /* an integer used as a boolean */ b = (1 < 2); /* (1 < 2) evaluates to true (1), b is assigned 1 */ if (b) /* Condition (1) evaluates to true */ { printf("This will be executed.\n"); }c int x = 0; int y = 2; int b; /* an integer used as a boolean */ b = (x && y); /* (0 && 2) evaluates to false (0), b is assigned 0 (lazy eval: y not checked) */ if (b) /* Condition (0) evaluates to false */ { printf("This will not be executed.\n"); }c int x = 0; int y = 2; int b; /* an integer used as a boolean */ b = (x || y); /* (0 || 2) evaluates to true (1), b is assigned 1 (lazy eval: y is checked) */ if (b) /* Condition (1) evaluates to true */ { printf("This will be executed.\n"); }
C11 Standard: The
boolTypeWith the C11 standard, a dedicated boolean type,
bool, is provided as an add-on in the<stdbool.h>library.When
<stdbool.h>is included:boolbecomes an alias for an integer type capable of storing boolean values.trueis defined as1.falseis defined as0.
Usage Example:
#include <stdbool.h> // Include for bool type bool a = true; bool b = false; bool c = (1 < 2); // c is assigned true (1)
Example: Using
boolfrom<stdbool.h>#include <stdio.h> #include <stdbool.h> // For the bool type #define THRESHOLD 10 int main(void) { bool b; // A boolean variable int x; printf("Enter an integer:\n"); scanf("%d", &x);// b evaluates to true if x is greater than THRESHOLD b = (x > THRESHOLD); if (b) { // Condition (true or false) printf("x is greater than %d\n", THRESHOLD); } return 0;}
Program to Find the Roots of a Quadratic Equation
Context
This problem is a classic example demonstrating the need for robust conditional logic to handle various cases.
The form of a quadratic equation is usually given as ax^2 + bx + c = 0. The roots are found using the quadratic formula: x = \frac{{-b \pm \sqrt{{b^2 - 4ac}}}}{{2a}}.
The nature and number of roots depend on the discriminant (the value under the square root): \Delta = b^2 - 4ac
If \Delta > 0: Two distinct real roots.
If \Delta = 0: One real root (a repeated root).
If \Delta < 0: Two complex roots.
Additionally, conditions like a=0 (making it a linear equation) must be handled.
Relevance
Such a problem will appear as a future practical question, requiring careful application of
if...else if...elsestructures to cover all possible scenarios for a, b, c values and the discriminant.
Test Yourself
Given three variables declared as follows:
int x = 0; int y = 10; int z = -40;What do the following boolean expressions evaluate to?
(i)
x && z(ii)
!x && z(iii)
(x > y) && (y > z)(iv)
!(x > 2 * y) && z(v)
(y < 4) || (z < 0) && !x
Given two boolean variables,
aandb, write down a C boolean expression which evaluates toa XOR b.Check that this works by writing a C program that uses the expression.