139 Converting Arithmetic Types This chapter introduces implicit type conversions, which are performed in C++ whenever different arithmetic types occur in expressions. Additionally, an operator for explicit type conversion is introduced. chapter 8 140 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES ■ IMPLICIT TYPE CONVERSIONS Integer promotions bool short char, signed char, unsigned char int int unsigned int unsigned short if int equals long if int equals short Type hierarchy Example short size(512); double res, x = 1.5; res = size / 10 * x; // short -> int -> double int long double double float unsigned long long unsigned int int not-existent, if int equals long IMPLICIT TYPE CONVERSIONS ■ 141 C++ allows you to mix arithmetic types in a single expression — in other words, the operands of an operator can belong to different types. The compiler automatically per- forms implicit type conversion, where a common type, which allows the operation in ques- tion to be performed, is assigned for the values of both operands. You can generally assume that the “smaller” type will be converted to the “larger” type. The assignment operator is an exception to this rule and will be discussed separately. The result of an arithmetic operation belongs to the common type used to perform the calculation. However, comparison expressions will be bool types no matter what type of operands are involved. ᮀ Integer Promotion Integer promotion is first performed for any expression: ■ bool, char, signed char, unsigned char, and short are converted to int ■ unsigned short is also converted to int if the int type is greater than short, and to unsigned int in all other cases. This type conversion is performed so as to preserve the original values. The boolean value false is converted to 0 and true is converted to 1. Thus, C++ will always use int type values or greater when performing calculations. Given a char variable c, the values of c and 'a' in the expression Example: c < 'a' will be converted to int before being compared. ᮀ Usual Arithmetic Type Conversions If operands of different arithmetic types still occur after integer promotion, further implicit type conversions along the lines of the hierarchy on the opposite page will be necessary. In this case, the type of the operand with the highest rank in the hierarchy is applied. These type conversions and integer promotions are collectively known as usual arithmetic type conversions. In our example, size/10 * x, the value of size is first promoted to int before an integer division size/10 is performed. The interim result 50 is then converted to dou- ble and multiplied by x. Usual arithmetic type conversions are performed for all binary operators and the con- ditional operator ?: provided the operands belong to an arithmetic type, the only excep- tions being the assignment operator and the logical operators && and ||. 142 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES Sign bit(= 0 ↔ not negative) Sign bit(= 0 ↔ not negative) 00001010 Extension to int (here 16 bit) The value 10 is preserved. 000000000000 1010 2 6 2 14 2 13 2 8 2 7 2 1 2 0 2 5 2 4 2 3 2 2 2 1 2 0 • • • • • • • • Binary representaion of the integer 10 as value of type signed char (8 bits): • • Sign bit(= 1 ↔ negative) Sign bit(= 1 ↔ negative) 11110110 Extension to int (here 16 bit) The value –10 is preserved. 111111111111 0110 2 6 2 14 2 13 2 8 2 7 2 1 2 0 2 5 2 4 2 3 2 2 2 1 2 0 ••• ••••• Binary representaion of the integer –10 as value of type signed char (8 bits): • • The value of a negative number changes if the bit pattern is interpreted as unsigned. The bit pattern 1111 0110 of –10, for example, corresponds to the unsigned char value 246 == 0*2 0 + 1*2 1 + 1*2 2 + 0*2 3 + 1*2 4 + 1*2 5 + 1*2 6 + 1*2 7 ✓ NOTE ■ PERFORMING USUAL ARITHMETIC TYPE CONVERSIONS Converting signed integers a) Converting a positive number b) Converting a negative number The bit pattern of –10 is computed by starting with the bit pattern of 10 and generat- ing the binary complement (see Binary Representation of Numbers in the appendix). PERFORMING USUAL ARITHMETIC TYPE CONVERSIONS ■ 143 Usual arithmetic type conversions retain the value of a number provided it can be repre- sented by the new type. The procedure for type conversion depends on the types involved: 1. Conversion of an unsigned type to a larger integral type Examples: unsigned char to int or unsigned int Zero extension is performed first. During this process, the bit pattern of the num- ber to be converted is expanded to match the length of the new type by adding zeros from the left. 2. Conversion of a signed type to a larger integral type ■ The new type is also signed Examples: char to int, short to long Signed integers are represented by generating the binary complement. The value is retained by performing sign extension. As shown in the example on the opposite page, the original bit pattern is expanded to match the length of the new type by padding the sign bit from the left. ■ The new type is unsigned Examples: char to unsigned int, long to unsigned long In this case the value of negative numbers is not retained. If the new type is of the same length, the bit pattern is retained. However, the bit pattern will be interpreted differently. The sign bit loses its significance (see the note oppo- site). If the new type is longer, sign extension is performed first and the new bit pattern is then interpreted as unsigned. 3. Conversion of an integral type to a floating-point type Examples: int to double, unsigned long to float The number is converted to an exponential floating-point type and the value retained. When converting from long or unsigned long to float, some rounding may occur. 4. Conversion of a floating-point type to a larger floating-point type Examples: float to double, double to long double The value is retained during this type conversion. 144 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES ■ IMPLICIT TYPE CONVERSIONS IN ASSIGNMENTS Example 1: int i = 100; long lg = i + 50; // Result of type int is // converted to long. Example 2: long lg = 0x654321; short st; st = lg; //0x4321 is assigned to st. Example 3: int i = –2; unsigned int ui = 2; i = i * ui; // First the value contained in i is converted to // unsigned int (preserving the bit pattern) and // multiplied by 2 (overflow!). // While assigning the bit pattern the result // is interpreted as an int value again, // i.e. –4 is stored in i. Example 4: double db = –4.567; int i; unsigned int ui; i = db; // Assigning –4. i = db – 0.5; // Assigning –5. ui = db; // –4 is incompatible with ui. Example 5: double d = 1.23456789012345; float f; f = d; // 1.234568 is assigned to f. IMPLICIT TYPE CONVERSIONS IN ASSIGNMENTS ■ 145 Arithmetic types can also be mixed in assignments. The compiler adjusts the type of the value on the right of the assignment operator to match the type of the variable on the left. In the case of compound assignments, calculations using normal arithmetic type con- versions are performed first before type conversion is performed following the rule for simple assignments. Two different cases can occur during type conversion in assignments: 1. If the type of the variable is larger than the type of the value to be assigned, the type of the value must be promoted. The rules for usual arithmetic type conver- sions are applied in this case (see Example 1). 2. If the type of the value to be assigned is larger, this type must be “demoted.” The following procedures are followed depending on individual circumstances: a. Conversion of an integral type to a smaller type: ■ the type is converted to a smaller type by removing the most significant byte(s). The bit pattern that remains will be interpreted as unsigned, if the new type is also unsigned, and as signed in all other cases. The value can only be retained if it can be represented by the new type (see Example 2). ■ when converting an unsigned type to a signed type of the same scale, the bit pattern is retained and will be interpreted as signed (see Example 3). b. Conversion of a floating-point type to an integral type The decimal part of the floating-point number is removed. For example, 1.9 converts to the integer 1. Rounding can be achieved by adding 0.5 to a posi- tive floating-point number or subtracting 0.5 from a negative floating-point number. This would allow for converting (1.9 + 0.5) to 2. If the resulting integer is too large or too small for the new type, the result is unpredictable. This particularly applies to converting negative floating- point numbers to unsigned integers (see Example 4). c. Conversion of a floating-point type to a smaller type If the floating-point number falls within the range of the new type, the value will be retained, although the accuracy may be compromised. If the value is too large to be represented by the new type, the result is unpredictable (see Example 5). 146 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES // Ellipse.cpp // The program draws an ellipse. // The points (x,y) on an ellipse with center (0,0) // and axes A and B satisfy: // x = A*cos(t), y = B*sint(t) for 0 <= t <= 2*PI . // #include <iostream> #include <cmath> // Prototypes of sin() and cos() using namespace std; #define CLS (cout << "\033[2J") #define LOCATE(z,s) (cout <<"\033["<<(z)<<';'<<(s)<<'H') #define DOT(x,y) (LOCATE(y,x) << '*') #define PI 3.1416 #define Mx 40 // The point (Mx, My) is the #define My 12 // center of the ellipse. #define A 25 // Length of main axis #define B 10 // Length of subsidiary axis int main() { int x, y; // Screen coordinates. CLS; // 0 <= t <= PI/2 is a 1/4-circle: for( double t = 0.0 ; t <= PI/2 ; t += 0.03) { x = (int) (A * cos(t) + 0.5); y = (int) (B * sin(t) + 0.5); DOT( x+Mx, y+My); DOT( x+Mx,-y+My); DOT(-x+Mx, y+My); DOT(-x+Mx,-y+My); } LOCATE(24,0); return 0; } ■ MORE TYPE CONVERSIONS Sample program MORE TYPE CONVERSIONS ■ 147 ᮀ Implicit Type Conversions in Function Calls In the case of function calls, arguments with arithmetic types are converted to the types of the corresponding parameters, similarly to conversions in assignments. Example: void func( short, double); // Prototype int size = 1000; // . . . func( size, 77); // Call The function func() has two parameters belonging to the short and double types. However, the function is called using two int arguments. This leads to implicit conver- sion of the value of size to short and the integer 77 to double. When an int is converted to short the compiler issues a warning, since some data loss may occur. You can use explicit type conversion to avoid warnings during type con- version. ᮀ Explicit Type Conversion It is possible to convert the type of an expression explicitly using the cast operator (type). Syntax: (type) expression This converts the value of an expression to the given type. Explicit type conversion is also known as casting. The cast operator (type) is a unary operator and thus has a higher precedence than the arithmetic operators. Example: int a = 1, b = 4; double x; x = (double)a/b; In this example the value of a is explicitly converted to a double. Following the con- ventions of usual implicit type conversion, b is also converted to double and a floating- point division is performed. The exact result, 0.25, is assigned to the variable x. Without casting, an integer division with a result of 0 would have occurred. C++ has additional operators for explicit type conversion—the cast operator dynamic_cast<>, for example. These operators, which are described in later chapters, are required for special circumstances, for example, to perform type checking at runtime when converting classes. exercises 148 ■ CHAPTER 8 CONVERTING ARITHMETIC TYPES // Convert.cpp —> Demonstrates type conversions. #include <iostream> #include <iomanip> using namespace std; int main() { char v_char = 'A'; cout << "v_char: " << setw(10) << v_char << setw(10) << (int)v_char << endl; short v_short = –2; cout << "v_short: " << dec << setw(10) << v_short << hex << setw(10) << v_short << endl; unsigned short v_ushort = v_short; cout << "v_ushort: " << dec << setw(10) << v_ushort << hex << setw(10) << v_ushort << endl; unsigned long v_ulong = v_short; cout << "v_ulong: " << hex << setw(20) << v_ulong << endl; float v_float = –1.99F; cout << "v_float: " << setw(10) << v_float << endl; cout << "(int)v_float: " << setw(10) << dec << (int)v_float << endl; return 0; } ■ EXERCISES Program listing for exercise 3 Graphic for exercise 4 The Sine Function Ÿ sin(x) Ő Ő 1 ******* Ő *** *** Ő ** ** Ő ** Ő ** ** Ő ** Ő ** ** Ő** 2PIx ņŐņņņņņņņ*ņņņņņņņŐņņņņņņņŐņņņņņņņŐņņņņņņņ*ņņņņņņņŐņņņņņņņŐņņņņņņņŐņņņņņņņ*ņņņņŹ Ő ** Ő ** ** Ő ** Ő ** ** Ő ** Ő ** ** Ő *** *** Ő -1 ******* Ő . to 1. Thus, C++ will always use int type values or greater when performing calculations. Given a char variable c, the values of c and &apos ;a& apos; in the expression Example: c < &apos ;a& apos; will. is also known as casting. The cast operator (type) is a unary operator and thus has a higher precedence than the arithmetic operators. Example: int a = 1, b = 4; double x; x = (double )a/ b; In. type to a signed type of the same scale, the bit pattern is retained and will be interpreted as signed (see Example 3). b. Conversion of a floating-point type to an integral type The decimal part