munotes®

Type Conversion and Typecasting

Chapter Fourteen

Syllabus topic 1, "Introduction: Algorithms, History of C, Structure of C Program. Program Characteristics, Compiler, Linker and preprocessor, pseudo code statements and flowchart symbols, Desirable program characteristics. Program structure. Compilation and Execution of a Program, C Character Set, identifiers and keywords, data types and sizes, constants and its types, variables, Character and character strings, typedef, typecasting"

Pages 64 to 68 of 222

In one line

Type conversion is C changing the type of a value for you, following fixed rules, and a typecast is you changing it deliberately by writing the type you want in brackets.

Why conversions happen at all

A processor cannot add an integer to a floating-point number. They are stored in completely different ways, and the instruction that adds two integers is not the instruction that adds two floating-point values. So when you write

double average = total / 3;

something has to give, and C has rules for deciding what. The rules are not arbitrary and they are not hard, and they are worth learning properly, because the cost of not knowing them is wrong answers that look plausible.

The two kinds

Implicit conversion, which C does on its own. Also called automatic conversion or coercion. It happens in four places: in an expression with mixed types, on assignment, when passing an argument to a function, and when returning a value.

Explicit conversion, which you write. This is a cast, and the syntax is the type name in brackets before the value:

(double) total
(int) 3.7
(char) 65

Rule 1: integer promotion

Anything narrower than int becomes an int before arithmetic happens to it. char, short, _Bool and the signed and unsigned versions of them are all promoted.

#include <stdio.h>

int main(void)
{
    char a = 100;
    char b = 100;

    printf("as char arithmetic would overflow, but this is int arithmetic: %d\n",
           a + b);
    printf("sizeof a is %zu, but sizeof (a + b) is %zu\n",
           sizeof a, sizeof(a + b));
    return 0;
}
as char arithmetic would overflow, but this is int arithmetic: 200
sizeof a is 1, but sizeof (a + b) is 4

a + b is 200, not an overflow, because both were promoted to int first. And sizeof(a + b) is the size of an int, which is the proof.

This is also why getchar returns an int, why a character constant has type int, and why 'A' + 1 prints as 66 with %d. Chapter 12 met the symptom; this is the cause.

Rule 2: the usual arithmetic conversions

When an operator has two operands of different types, the narrower one is converted to the wider. The order, from widest down, for the types you will use:

long double  >  double  >  float  >  unsigned long long  >  long long
 >  unsigned long  >  long  >  unsigned int  >  int

So in an expression with a double and an int, the int becomes a double and the arithmetic is floating-point. In an expression with two ints, the arithmetic is integer, whatever you are going to do with the answer.

munotes.in64

Type Conversion and Typecasting

That last sentence is where the marks are lost. The type of an expression is decided by the operands, not by what you assign it to.

#include <stdio.h>

int main(void)
{
    int total = 7;
    int count = 2;

    double wrong = total / count;
    double right = (double) total / count;

    printf("7 / 2 assigned to a double  : %.4f\n", wrong);
    printf("(double) 7 / 2              : %.4f\n", right);
    printf("7 %% 2 is %d, so the fraction thrown away was %d/2\n",
           total % count, total % count);
    return 0;
}
7 / 2 assigned to a double  : 3.0000
(double) 7 / 2              : 3.5000
7 % 2 is 1, so the fraction thrown away was 1/2

total / count is integer division, giving 3. Assigning 3 to a double gives 3.0. The fraction was gone before the assignment happened, and no amount of double on the left recovers it.

The fix is to make one operand floating-point. (double) total / count promotes count too, by rule 2. So does total / (double) count, and so does total * 1.0 / count.

Rule 3: assignment converts to the left-hand type

On assignment the value is converted to the type of the variable, and if it will not fit, something is lost. Two cases matter.

Floating-point to integer: the fraction is discarded, not rounded.

#include <stdio.h>
#include <math.h>

int main(void)
{
    printf("(int) 3.7   is %d\n", (int) 3.7);
    printf("(int) 3.2   is %d\n", (int) 3.2);
    printf("(int) -3.7  is %d\n", (int) -3.7);
    printf("round(3.7)  is %.0f\n", round(3.7));
    printf("round(-3.7) is %.0f\n", round(-3.7));
    return 0;
}
(int) 3.7   is 3
(int) 3.2   is 3
(int) -3.7  is -3
round(3.7)  is 4
round(-3.7) is -4

Truncation is towards zero, so (int) -3.7 is -3 and not -4. If you want rounding, use round from <math.h>; if you want the largest integer no greater than the value, use floor.

Integer to a narrower integer: the high bits are discarded. Assigning 300 to a char does not give you 300 and does not give you an error. -Wall -Wextra will usually warn, which is the reason to compile with them.

What a cast is for, and when to use one

Four legitimate uses, and they are worth knowing as a list, because a cast is also a way of telling the compiler to stop objecting, which is a way of hiding a bug.

1. To force floating-point arithmetic. The most common use in this course.

double average = (double) total / count;

2. To discard a fraction deliberately, where truncation is what you mean.

int whole_rupees = (int) amount;

3. To pick the right overload of a library function, or to satisfy a parameter type, most often (double) for a float or (int) for a char in a printf argument.

munotes.in65

Type Conversion and Typecasting

4. To say "I know this is unused", with (void).

(void) unused_parameter;

A cast used to silence a warning you do not understand is a bug you have hidden. -Wall warning about a conversion is usually telling you the truth.

The worked example: the average of three marks

This is the shape of several of the lab's practicals, and it is where the rule bites.

#include <stdio.h>

int main(void)
{
    int m1 = 63, m2 = 58, m3 = 72;
    int total = m1 + m2 + m3;

    printf("total is %d\n", total);
    printf("wrong  : total / 3           = %d\n", total / 3);
    printf("wrong  : assigned to a double = %.4f\n", (double) (total / 3));
    printf("right  : (double) total / 3   = %.4f\n", (double) total / 3);
    printf("right  : total / 3.0          = %.4f\n", total / 3.0);
    return 0;
}
total is 193
wrong  : total / 3           = 64
wrong  : assigned to a double = 64.0000
right  : (double) total / 3   = 64.3333
right  : total / 3.0          = 64.3333

Both correct forms agree, and both differ from the wrong one. Read the third line carefully: casting after the division changes nothing, because the division has already happened. The cast has to be on an operand.

What this does NOT mean

A cast does not change the variable. (int) x produces a new value of type int; x is untouched and still a double. A cast is an operator producing a result, not an instruction to the variable.

Conversion is not rounding. Converting a floating-point value to an integer discards the fractional part, towards zero.

Assigning to a double does not make the arithmetic floating-point. The expression's type was fixed by its operands before the assignment was reached.

An implicit conversion is not an error, and that is the problem. C converts silently in most cases, so a lost fraction or a truncated value does not stop the program. -Wall -Wextra is what makes the risky ones visible.

A cast between unrelated pointer types is not a conversion of the data. It reinterprets the bits. That is a later topic and it is not what this chapter is about.

char to int is not "losing information". It is a widening, and it is exact. The losing direction is int to char.

Quick revision

  • Implicit conversion is C's; explicit conversion is a cast, written (type) value.
  • Rule 1, integer promotion: anything narrower than int becomes int before arithmetic.
  • Rule 2, usual arithmetic conversions: the narrower operand is converted to the wider. long double down to int.
  • Rule 3, assignment: the value is converted to the left-hand type, and may lose data.
  • The type of an expression is decided by its operands, never by what it is assigned to.
  • 7 / 2 is 3 in any context. (double) 7 / 2 is 3.5.
  • Floating-point to integer truncates towards zero: (int) -3.7 is -3. Use round to round.
  • A cast applies to an operand, so it must be written before the division, not around it.
  • Compile with -Wall -Wextra: most damaging conversions warn.
munotes.in66

Type Conversion and Typecasting

Test yourself

1. What does printf("%d\n", 5 / 2) print, and what does printf("%.2f\n", 5 / 2.0) print?

2 and 2.50. The first is integer division; in the second one operand is a double, so the other is converted and the division is floating-point.

2. double avg = sum / n; where both are int gives a whole number. Fix it two ways.

double avg = (double) sum / n; or double avg = sum / (double) n;. Either makes one operand a double, and rule 2 converts the other.

3. What is (int) -2.9?

-2. Truncation is towards zero.

4. What is the type and value of 'a' + 1?

int, and 98 on an ASCII machine. The char is promoted to int before the addition.

5. char c = 300; compiles with a warning. What is in c?

An implementation-defined value: the high bits of 300 do not fit in one byte and are discarded. It is not 300, and the warning is telling you so.

6. Why does (double) (total / 3) not give you the fractional average?

Because the division inside the brackets is integer division and has already thrown the fraction away. The cast then converts the whole number 3 to 3.0.

7. Name the four places an implicit conversion happens.

In a mixed-type expression, on assignment, when passing an argument to a function, and when returning a value.

What can be asked on this, and how to answer it

"What is type conversion? Explain implicit and explicit conversion with examples." Define both, give the three rules with one example each, and give (double) total / count as the explicit case that matters. Say that the expression's type comes from its operands, because that sentence is what the question is really testing.

"What is typecasting? Give its syntax and two uses." A cast converts a value to a named type at a point you choose, written (type) expression. Two uses: forcing floating-point division, and deliberately discarding a fraction. Add that a cast produces a value and does not change the variable.

"Explain the rules of automatic type conversion in an expression." Integer promotion first, then the usual arithmetic conversions towards the wider type, then, on assignment, conversion to the left-hand type with possible loss. Give the ordering from long double down to int.

munotes.in67

Type Conversion and Typecasting

"Write a program to find the average of three integers and explain the conversion involved." Give the program in this chapter, and explain that total / 3 is integer division, so one operand must be made a double before dividing.

"What is the difference between (int) 3.7 and round(3.7)?" The cast truncates towards zero, giving 3. round from <math.h> rounds to the nearest, giving 4.0 as a double. For -3.7 they give -3 and -4.

munotes.in68

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Report or request
Done!