• No results found

Mixed Type Expressions

In document C++ (Page 57-61)

The program’s output is

one = 1, one_tenth = 0.1, zero = 1.38778e-016

Surely the reported answer (1.38778 × 10−16is close to the correct answer (zero). If you round it to the one- hundred trillionth place (15 places behind the decimal point), it is correct. Listing 5.5 (samedifferent.cpp) in Chapter 5 further emphasizes the point that programmers must use care when dealing with the imprecision of floating-point quantities.

Despite their inexactness, double-precision floating-point numbers are used every day throughout the world to solve sophisticated scientific and engineering problems. The limitations of floating-point numbers are unavoidable since values with infinite characteristics must be represented in a finite way. Floating-point numbers provide a good trade-off of precision for practicality.

Since acharis stored as a number (see Section 3.7), arithmetic can be performed on characters. We will have little need to apply mathematics to characters, but sometimes it is useful. As an example, the lower-case letters of the alphabet a–z occupy ASCII values 97–123, with a = 97, b = 98, etc. The upper- case letters A–Z are coded as 65–91, with A = 65, B = 66, etc. To capitalize any lower-case letter, you need only subtract 32, as in

char lower = 'd', upper = lower - 32; cout << upper << endl;

This section of code would print D. If you do not remember the offset of 32 between upper- and lower-case letter, you can compute it with the letters themselves:

upper = lower - ('a' - 'A');

In this case, if lower has been assigned any value in the range'a'to'z', upper will be assigned the capitalized version of lower. On the other hand, if lower’s value is outside of that range, upper will not be assigned a meaningful value.

4.2

Mixed Type Expressions

Expressions may contain mixed elements; for example, in the following program fragment int x = 4;

double y = 10.2, sum; sum = x + y;

anintis being added to adouble, and the result is being assigned to adouble. How is the arithmetic performed?

As shown in Figure 4.1, the range ofints falls completely within the range ofdoubles; thus, any intvalue can represented by adouble. Theint4 also can be expressed as thedouble4.0. In fact, since the largestinton most systems is 2,147,483,647, the minimum 15 digits ofdoubleprecision are more than adequate to represent all integers exactly. This means that anyintvalue can be represented by adouble. The converse is not true, however. 2,200,000,000 can be represented by adoublebut it is too big for theinttype. We say that thedoubletype is wider than theinttype and that theinttype is narrowerthan thedoubletype.

4.2. MIXED TYPE EXPRESSIONS 47

0

+2.1 × 10

9

-2.1 × 10

9

+1.8 × 10

308

+1.8 × 10

308

Range of int

Range of double

Figure 4.1: Range of ints vs. range of doubles

It would be reasonable, then, to be able to assign intvalues todoublevariables. The process is called widening, and it is always safe to widen anintto adouble. The following code fragment

double d1;

int i1 = 500; d1 = i1;

cout << "d1 = " << d1 << endl;

is legal C++code, and when part of a complete program it would display

d1 = 500

Assigning adoubleto anintvariable is not always possible, however, since thedoublevalue may not be in the range ofints. Furthermore, if thedoublefalls within the range ofints but is not a whole number, the fractional part cannot be represented by theint. Consider the following code fragment: double d = 1.6;

int i = d;

The second line assigns 1 to i. The fractional part (0.6) is truncated (see Section 4.1). Note that proper rounding is not done. The Visual C++compiler will warn us of a potential problem:

warning C4244: ’=’ : conversion from ’double’ to ’int’, possible loss of data

This warning reminds us that some information may be lost in the assignment. While the compiler and linker will generate a executable program when warnings are present, all warnings should be carefully scrutinized. This warning is particularly useful, since it is easy for errors to creep into calculations due to the truncation of floating-point numbers.

Converting from a wider type to a narrower type (likedoubletoint) is called narrowing. It often is necessary to assign a floating-point value to an integer variable. If we know the value to assign is within the range ofints, and the value has no fractional parts or its truncation would do no harm, the assignment would be safe. To perform the assignment without a warning from the compiler, we use a procedure called a cast, also called a type cast. The cast forces the compiler to accept the assignment without a issuing a warning. Thestatic_castoperator is used during the assignment:

4.2. MIXED TYPE EXPRESSIONS 48

i = static_cast<int>(d);

The reserved wordstatic_cast is used to perform a narrowing conversion. The item to convert is placed in the parentheses (in this case the variable d), and the desired type is placed in the angle brackets (in this case the typeint). In the statement

i = static_cast<int>(d);

the type of the variable d is not changed; its value is simply copied to a temporary location in memory. During the copying process the value is converted to anint.

Literal values and expressions can also be cast:

i = static_cast<int>(1.6); i = static_cast<int>(x + 2.1);

Whendoubles are narrowed toints, any fractional part is discarded. The as- signment truncates; it does not round. The value 1.7 would be converted to the intvalue 1.

Converting from a wider type to a narrower type (like doubleto int) is called narrowing. The opposite conversion, from a narrower type to a wider type, is called widening. The widening conversion is always safe, so a type cast is not required. Narrowing is a potentially dangerous operation, and using an explicit cast does not remove the danger—it simply silences the compiler.

Narrowing is a potentially dangerous operation, and using an explicit cast does not remove the danger— it simply silences the compiler. For example, consider Listing 4.4 (badnarrow.cpp).

Listing 4.4: badnarrow.cpp 1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 double d = 2200000000.0; 8 int i = d; 9 cout << "d = " << d << ", i = " << i << endl; 10 }

The Visual C++compiler issues a warning about the possible loss of precision when assigning d to i. Silencing the warning with a type cast is a bad idea; the program’s output indicates that the warning should be heeded:

4.2. MIXED TYPE EXPRESSIONS 49

The printed values of i and d are not even close, nor can they be because it is impossible to represent the value 2,200,000,000 as aninton a system that uses 32-bit integers. When assigning a value of a wider type to a variable of a narrower type, the programmer must assume the responsibility to ensure that the actual value to be narrowed is indeed within the range of the narrower type. The compiler cannot ensure the safety of the assignment.

Casts should be used with great care because a cast creates a spot in the program that is immune to the compiler’s type checking. A careless assignment can produce a garbage result introducing an error into the program.

When mixed arithmetic must be performed—such as adding anintto adouble—the compiler auto- matically produces machine language code that copies theintvalue to a temporary memory location and transforms it into itsdoubleequivalent. Double-precision floating-point arithmetic is then performed. In- teger arithmetic is performed only when both operands andints. 1/3 thus evaluates to 0, but 1.0/3.0,

1/3.0, and 1.0/3 all evaluate to 0.33333.

Sincedoubleis wider thanint, we say thatdoubledominatesint. In a mixed type arithmetic expression, the less dominant type is coerced into the more dominant type in order to perform the arithmetic operation.

Section 3.8 introduced enumerated types. Behind the scenes, the compiler translates enumerated values into integers. The first value in the enumeration is 0, the second value is 1, etc. The following code demonstrates the relationship between enumerated types and integers:

enum Color { Red, Orange, Yellow, Green, Blue, Violet }; cout << Orange << " " << Green << endl;

The output of this code fragment is

1 3

C++allows a programmer to assign anenumtype to an integer variable; for example, the following state- ment is legal:

int num = Orange;

Here, the variable num assumes the value 1. A programmer, however, may not directly assign an integer value to a variable declared to be of anenumtype; for example, the following statement:

Color col = 1;

is illegal. Sometimes it is convenient to convert an integer to an enumerated type, and a type cast enables the assignment:

enum Color { Red, Orange, Yellow, Green, Blue, Violet }; Color myColor = static_cast<Color>(2);

Since the compiler encodes enumerated values with the integers 0, 1, 2, . . . in the order the values are spec- ified in the enumeration list, the assignment statement above assigns the value Yellow to the variable

myColor. Casting, of course, can be dangerous; consider:

In document C++ (Page 57-61)