Working with Arithmetic Expressions in C Programming Language

In C, just as in virtually all programming languages, the plus sign (+) is used to add two values, the minus sign (–) is used to subtract two values, the asterisk (*) is used to multi- ply two values, and the slash (/) is used to divide two values. These operators are known as binary arithmetic operators because they operate on two values or terms.

You have seen how a simple operation such as addition can be performed in C. Program 4.2 further illustrates the operations of subtraction, multiplication, and division. The last two operations performed in the program introduce the notion that one opera- tor can have a higher priority, or precedence, over another operator. In fact, each operator in C has a precedence associated with it. This precedence is used to determine how an expression that has more than one operator is evaluated: The operator with the higher precedence is evaluated first. Expressions containing operators of the same precedence

are evaluated either from left to right or from right to left, depending on the operator. This is known as the associative property of an operator. Appendix A provides a complete list of operator precedences and their rules of association.

Program 4.2   Using the Arithmetic  Operators

// Illustrate the use of various arithmetic operators

#include <stdio.h>

int main (void)

{

Program 4.2   Continued

int a = 100;

int b = 2;

int c = 25;

int d = 4;

int result;

result = a – b;    // subtraction

printf (“a – b = %i\n”, result);

result = b * c;    // multiplication

printf (“b * c = %i\n”, result);

result = a / c;    // division

printf (“a / c = %i\n”, result);

result = a + b * c; // precedence

printf (“a + b * c = %i\n”, result);

printf (“a * b + c * d = %i\n”, a * b + c * d);

return 0;

}

Program 4.2   Output

a – b = 98 b * c = 50 a / c = 4

a + b * c = 150

a * b + c * d = 300

After declaring the integer variables a, b, c, d, and result, the program assigns the result of subtracting b from a to result and then displays its value with an appropriate printf call.

The next statement

result = b * c;

has the effect of multiplying the value of b by the value of c and storing the product in result. The result of the multiplication is then displayed using a printf call that should be familiar to you by now.

The next program statement introduces the division operator—the slash. The result of 4, as obtained by dividing 100 by 25, is displayed by the printf statement immediately following the division of a by c.

On some computer systems, attempting to divide a number by zero results in abnor- mal termination of the program.2 Even if the program does not terminate abnormally, the results obtained by such a division will be meaningless.

In Chapter 6, you see how you can check for division by zero before the division operation is performed. If it is determined that the divisor is zero, an appropriate action can be taken and the division operation can be averted.

The expression

a + b * c

does not produce the result of 2550 (102 ´ 25); rather, the result as displayed  by the cor- responding printf statement is shown as 150. This is because C, like most other pro- gramming languages, has rules for the order of evaluating multiple operations or terms in an expression. Evaluation of an expression generally proceeds from left to right.

However, the operations of multiplication and division are given precedence over the operations of addition and subtraction. Therefore, the expression

a + b * c

is evaluated as

a + (b * c)

by the C system. (This is the same way this expression would be evaluated if you were to apply the basic rules of algebra.)

If you want to alter the order of evaluation of terms inside an expression, you can use parentheses. In fact, the expression listed previously is a perfectly valid C expression. Thus, the statement

result = a + (b * c);

could have been substituted in Program 4.2 to achieve identical results. However, if the expression

result = (a + b) * c;

were used instead, the value assigned to result would be 2550 because the value of a (100) would be added to the value of b (2) before multiplication by the value of c (25) would take place. Parentheses can also be nested, in which case evaluation of the expres- sion proceeds outward from the innermost set of parentheses. Just be certain you have as many closed parentheses  as you have open ones.

You will notice from the last statement in Program 4.2 that it is perfectly valid to give an expression  as an argument to printf without having to first assign the result of the expression evaluation to a variable. The expression

a * b + c * d

is evaluated according to the rules stated previously  as

(a * b) + (c * d)

or

(100 * 2) + (25 * 4)

The result of 300 is handed to the printf routine.

1. Integer Arithmetic  and the Unary Minus Operator

Program 4.3 reinforces what you just learned and introduces the concept of integer arithmetic.

Program 4.3   More Examples with Arithmetic  Operators

// More arithmetic expressions

#include <stdio.h>

int main (void)

{

int  a = 25;

int  b = 2;

float c = 25.0;

float d = 2.0;

printf (“6 + a / 5 * b = %i\n”, 6 + a / 5 * b);

printf (“a / b * b = %i\n”, a / b * b);

printf (“c / d * d = %f\n”, c / d * d);

printf (“-a = %i\n”, -a);

return 0;

}

Program 4.3   Output

6 + a / 5 * b = 16

a / b * b = 24

c / d * d = 25.000000

-a = -25

Extra blank spaces are inserted between int and the declaration of a, b, c, and d in the first four statements to align the declaration of each variable. This helps make the pro- gram more readable.You also might have noticed in each program presented thus far that a blank space was placed around each operator. This, too, is not required and is done solely for aesthetic reasons. In general, you can add extra blank spaces just about any- where that a single blank space is allowed. A few extra presses of the spacebar proves worthwhile if the resulting program is easier to read.

The expression in the first printf call of Program 4.3 reinforces the notion of opera- tor precedence. Evaluation of this expression proceeds as follows:

  1. Because division has higher precedence than addition, the value of a (25) is divid- ed by 5 first. This gives the intermediate result of 5.
  2. Because multiplication also has higher precedence than addition, the intermediate result of 5 is next multiplied by 2, the value of b, giving a new intermediate result of 10.
  3. Finally, the addition of 6 and 10 is performed, giving a final result of 16.

The second printf statement introduces a new twist.You would expect that dividing a by b and then multiplying by b would return the value of a, which has been set to 25. But this does not seem to be the case, as shown by the output display of 24. It might seem like the computer lost a bit somewhere along the way. The fact of the matter is that this expression was evaluated using integer arithmetic.

If you glance back at the declarations for the variables a and b, you will recall that they were both declared to be of type int. Whenever a term to be evaluated in an expression consists of two integers, the C system performs the operation using integer arithmetic. In such a case, all decimal portions of numbers are lost. Therefore, when the value of a is divided by the value of b, or 25 is divided by 2, you get an intermediate result of 12 and not 12.5 as you might expect. Multiplying this intermediate result by 2 gives the final result of 24, thus explaining the “lost” digit. Don’t forget that if you divide two integers, you always get an integer result.

As can be seen from the next-to-last printf statement in Program 4.3, if you per- form the same operation using floating-point values instead of integers, you obtain the expected result.

The decision of whether to use a float variable or an int variable should be made based on the variable’s intended use. If you don’t need any decimal places, use an integer variable. The resulting program is more efficient—that  is, it executes more quickly on many computers. On the other hand, if you need the decimal place accuracy, the choice is clear. The only question you then must answer is whether to use a float, double, or long double. The answer to this question depends on the desired accuracy of the num- bers you are dealing with, as well as their magnitude.

In the last printf statement, the value of the variable a is negated by use of the unary minus operator. A unary operator is one that operates on a single value, as opposed to a binary operator, which operates on two values. The minus sign actually has a dual role: As a binary operator, it is used for subtracting two values; as a unary operator, it is used to negate a value.

The unary minus operator has higher precedence than all other arithmetic operators, except for the unary plus operator (+), which has the same precedence. So the expression

c = -a * b;

results in the multiplication of –a by b. Once again, in Appendix A you will find a table summarizing the various operators and their precedences.

2. The Modulus Operator

The next arithmetic to be presented in this chapter is the modulus operator, which is symbolized by the percent sign (%). Try to determine how this operator works by analyz- ing Program 4.4.

Program 4.4   Illustrating the Modulus Operator

// The modulus operator

#include <stdio.h>

int main (void)

{

int a = 25, b = 5, c = 10, d = 7;

printf (“a %% b = %i\n”, a % b);

printf (“a %% c = %i\n”, a % c);

printf (“a %% d = %i\n”, a % d);

printf (“a / d * d + a %% d = %i\n”,a / d * d + a % d);

return 0;

}

Program 4.4   Output

a % b = 0 a % c = 5 a % d = 4

a / d * d + a % d = 25

The first statement inside main defines and initializes the variables a, b, c, and d in a sin- gle statement.

As you know,  printf uses the character that immediately follows the percent sign to determine how to print the next argument. However, if it is another percent sign that follows, the printf routine takes this as an indication that you really intend to display a percent sign and inserts one at the appropriate place in the program’s output.

You are correct if you concluded that the function of the modulus operator % is to give the remainder of the first value divided by the second value. In the first example, the remainder after 25 is divided by 5 and is displayed  as 0. If you divide 25 by 10, you get a remainder of 5, as verified by the second line of output. Dividing 25 by 7 gives a remainder of 4, as shown in the third output line.

The last line of output in Program 4.4 requires a bit of explanation. First, you will notice that the program statement has been written on two lines. This is perfectly valid in C. In fact, a program statement can be continued to the next line at any point at which a blank space could be used. (An exception to this occurs when dealing with character strings—a topic discussed in Chapter 10, “Character Strings.”) At times, it might not only be desirable, but perhaps even necessary to continue a program statement onto the next line. The continuation of the printf call in Program 4.4 is indented to visually show that it is a continuation of the preceding program statement.

Turn your attention to the expression evaluated in the final statement.You will recall that any operations between two integer values in C are performed with integer arith- metic. Therefore, any remainder resulting from the division of two integer values is simply discarded. Dividing 25 by 7, as indicated by the expression a / d, gives an inter- mediate result of 3. Multiplying this value by the value of d, which is 7, produces the intermediate result of 21. Finally, adding the remainder of dividing a by d, as indicated by the expression a % d, leads to the final result of 25. It is no coincidence that this value is the same as the value of the variable a. In general, the expression

a / b * b + a % b

will always equal the value of a, assuming of course that a and b are both integer values. In fact, the modulus operator % is defined to work only with integer values.

As far as precedence is concerned, the modulus operator has equal precedence to the multiplication and division operators. This implies, of course, that an expression such as

table + value % TABLE_SIZE

will be evaluated as

table + (value % TABLE_SIZE)

3. Integer and Floating-Point Conversions

To effectively develop C programs, you must understand the rules used for the implicit conversion of floating-point and integer values in C. Program 4.5 demonstrates some of the simple conversions between numeric data types.You should note that some compil- ers might give warning messages to alert you of the fact that conversions are being per- formed.

Program 4.5   Converting  Between  Integers and Floats

// Basic conversions in C

#include <stdio.h>

int main (void)

{

float f1 = 123.125, f2;

int   i1, i2 = -150;

char  c = ‘a’;

i1 = f1;             // floating to integer conversion

printf (“%f assigned to an int produces %i\n”, f1, i1);

f1 = i2;             // integer to floating conversion

printf (“%i assigned to a float produces %f\n”, i2, f1);

f1 = i2 / 100;       // integer divided by integer

printf (“%i divided by 100 produces %f\n”, i2, f1);

f2 = i2 / 100.0;       // integer divided by a float

printf (“%i divided by 100.0 produces %f\n”, i2, f2);

f2 = (float) i2 / 100;  // type cast operator

printf (“(float) %i divided by 100 produces %f\n”, i2, f2);

return 0;

}

Program 4.5   Output

123.125000 assigned to an int produces 123

-150 assigned to a float produces -150.000000

-150 divided by 100 produces -1.000000

-150 divided by 100.0 produces -1.500000 (float)

-150 divided by 100 produces -1.500000

Whenever a floating-point value is assigned to an integer variable in C, the decimal portion of the number gets truncated. So, when the value of f1 is assigned to i1 in the previous program, the number 123.125 is truncated, which means that only its integer portion, or 123, is stored in i1. The first line of the program’s output verifies that this is the case.

Assigning an integer variable to a floating variable does not cause any change in the value of the number; the value is simply converted by the system and stored in the float- ing variable. The second line of the program’s output verifies that the value of i2 (–150) was correctly converted and stored in the float variable f1.

The next two lines of the program’s output illustrate two points that must be remem-bered when forming arithmetic expressions. The first has to do with integer arithmetic, which was previously discussed in this chapter. Whenever two operands in an expression are integers (and this applies to short, unsigned, long, and long long integers as well), the operation is carried out under the rules of integer arithmetic. Therefore, any decimal portion resulting from a division operation is discarded, even if the result is assigned to a floating variable (as you did in the program). Therefore, when the integer variable i2 is divided by the integer constant 100, the system performs the division as an integer divi- sion. The result of dividing –150 by 100, which is –1, is, therefore, the value that is stored in the float variable f1.

The next division performed in the previous listing involves an integer variable and a floating-point constant. Any operation between two values in C is performed as a float- ing-point operation if either value is a floating-point variable or constant. Therefore, when the value of i2 is divided by 100.0, the system treats the division as a floating- point division and produces the result of –1.5, which is assigned to the float variable f1.

4. The Type Cast Operator

The last division operation from Program 4.5 that reads

f2 = (float) i2 / 100;  // type cast operator

introduces the type cast operator. The type cast operator has the effect of converting the value of the variable i2 to type float for purposes of evaluation of the expression. In no way does this operator permanently affect the value of the variable i2; it is a unary oper- ator that behaves like other unary operators. Because the expression –a has no perma- nent effect on the value of a, neither does the expression (float) a.

The type cast operator has a higher precedence than all the arithmetic operators except the unary minus and unary plus. Of course, if necessary, you can always use parentheses in an expression to force the terms to be evaluated in any desired order.

As another example of the use of the type cast operator, the expression

(int) 29.55 + (int) 21.99

is evaluated in C as

29 + 21

because the effect of casting a floating value to an integer is one of truncating the floating-point value. The expression

(float) 6 / (float) 4

produces a result of 1.5, as does the following expression:

(float) 6 / 4

Source: Kochan Stephen G. (2020), Programming in C: A Complete Introduction to the C Programming Language, Sams; Subsequent edition.

Leave a Reply

Your email address will not be published. Required fields are marked *