Have you ever wanted to learn how basic types of C++ variables interact in complex situations? Ivor Horton explains this, and also describes some interesting features of C++. This article is from chapter 3 of Ivor Horton's Beginning ANSC C++ The Complete Language (Apress, 2004; ISBN 1590592271).

More on Handling Basic Data Types - Try It Out: Explicit Casting (Page 2 of 13 )

Suppose you need to be able to convert a length in yards (as a decimal value) to yards, feet, and inches (as integer values). You can put together a program to do this:

// Program 3.1 Using Explicit Casts #include <iostream> using std::cin; using std::cout; using std::endl; int main() { const long feet_per_yard = 3; const long inches_per_foot = 12; double yards = 0.0; // Length as decimal yards long yds = 0; // Whole yards long ft = 0; // Whole feet long ins = 0; // Whole inches cout << "Enter a length in yards as a decimal: "; cin >> yards; // Get the length as yards, feet, and inches yds = static_cast<long>(yards); ft = static_cast<long>((yards - yds) *feet_per_yard); ins = static_cast<long> (yards * feet_per_yard * inches_per_foot)% inches_per_foot; cout << endl << yards << " yards converts to " << yds << " yards " << ft << " feet " << ins << " inches."; cout << endl; return 0; }

The first two statements in main() declare a couple of conversion constants that you’ll use:

const long feet_per_yard = 3; const long inches_per_foot = 12;

You declare these variables as const to prevent them from being modified accidentally in the program, and you use the type long to be consistent with the other values. Although the type short would have been adequate to store these values, using it may actually increase (rather than decrease) the size of your program in the long run. This is because of additional, implicit conversions that may be necessary when using them in expressions with other integer types.

The next four declarations define the variables you’ll use in the calculation:

double yards = 0.0; // Length as decimal yards long yds = 0; // Whole yards long ft = 0; // Whole feet long ins = 0; // Whole inches

You prompt for the required input and then read a value from the keyboard with these statements:

cout << "Enter a length in yards as a decimal: "; cin >> yards;

The next statement computes the whole number of yards from the input value with an explicit cast:

yds = static_cast (yards);

The cast to type long discards the fractional part of the value in yards and stores the integral result in yds. If you omit the explicit cast here, some compilers will compile the program without warning you that they’ve inserted the required conversion, even though there’s clearly a potential loss of data in this conversion operation. You should always write an explicit cast in such cases to indicate that you intend this to happen. If you leave it out, it’s not clear that you realized the need for the conversion and the potential loss of data.

You obtain the number of whole feet in the length with the following statement:

ft = static_cast ((yards – yds) * feet_per_yard);

You want the number of whole feet that aren’t contained in the whole yards, so you subtract the value in yds from yards. The compiler will arrange for the value in yds to be converted automatically to type double for the subtraction, and the result will be of type double as well. The value of feet_per_yard will then be converted automatically to double to allow the multiplication to take place, and finally your explicit cast will be applied to the result to convert it from type double to type long.

The final part of the calculation is to obtain the residual number of whole inches:

ins = static_cast<long> (yards * feet_per_yard * inches_per_foot) % inches_per_foot;

This is done by calculating the total number of inches in the original length, converting this to type long with an explicit cast, and then getting the remainder after dividing by the number of inches in a foot.

Lastly, you output the results with the following statement:

cout << std::endl << yards << " yards converts to " << yds << " yards" << ft << " feet " << ins << " inches.";

Old-Style Casts

Prior to the introduction of static_cast<>() (and the other casts, const_cast<>(), dynamic_cast<>(), and reinterpret_cast<>(), which I discuss later in the book) into C++, an explicit cast of the result of an expression to another type was written like this:

(the_type_to_convert_to)expression

The result of expression is cast to the type between the parentheses. For example, the statement to calculate ins in the previous example could be written like this:

ins = (long)(yards * feet_per_yard * inches_per_foot) % inches_per_foot;

Essentially, there are four different kinds of casts, and the old-style casting syntax covers them all. Because of this, code using the old-style casts is more prone to error— it isn’t always clear what you intended, and you may not get the result you expected. Although you’ll still see old-style casting used extensively (it’s still part of the language), I strongly recommend that you stick to using only the new casts in your code.

More on Pseudo-Random Number Generation

Now that you know about casting, you can make sure that you don’t run into difficulties with using a value returned by the rand()in an arithmetic expression. I noted in the last chapter that rand()returns values from 0 to RAND_MAX, and RAND_MAX could be defined as any positive int value up to the maximum in the range. Assuming type long has a greater range than type int, you can avoid any possible problems when you want to perform arithmetic with a random integer by casting the value that is returned by the function to type long, for example:

long even = 2*static_cast<long>(std::rand());

With the value from rand() as type long, the multiply operation will be carried out after converting the value 2 to the same type. Thus the result of the multiplication will always be within range. You can produce the same effect by defining the literal as type long:

long even = 2L* std::rand();

Because 2L is of type long, the compiler will arrange to cast the value that is returned by rand() to type long before executing the multiply operation.

You could use rand() to obtain random integers in a more limited range than 0 to RAND_MAX. For instance, suppose you wanted random integers from to 0 to 10 inclusive. You could generate that from the value returned by the rand() function:

const int limit = 11; int random_value = static_cast<int>( (limit*static_cast<long> (std::rand()))/(RAND_MAX+1L));

What you’re effectively doing here is dividing the range 0 to RAND_MAX into limit segments, in which all the values returned by rand() within a given segment will result in one of the values in the range 0 to limit-1 inclusive. You do this by multiplying limit by the ratio rand()/(RAND_MAX+1L). You divide by RAND_MAX+1L rather than RAND_MAX to deal with the case in which rand() returns a value that is exactly RAND_MAX. If you were to divide by RAND_MAX, the result would be limit in this singular case, instead of limit-1. The constant 1L that you add to RAND_MAX is of type long, so RAND_MAX will be converted to type long too before the addition is carried out. As I’ve already said, RAND_MAX is defined to be the largest possible integer of type int with some implementations of rand(). In this case, you can’t add 1 and get a correct result without converting to type long first.

If you want your random values to be between 1 and some upper limit, rather than having a lower limit of 0, this is also very easy to arrange:

const int limit = 100; int random_value = static_cast<int>( 1L+(limit*static_cast<long>(std::rand ()))/(RAND_MAX+1L));

Here you use the same expression as you did previously to produce values from 0 to limit-1 inclusive, and you add 1 to produce values from 1 to limit.

As I said at the beginning, all this assumes that type long has a greater range than type int. If this isn’t the case and you need to generate random values outside the range of type int, your only recourse is to cast the value returned by rand() to a floating-point type and store the result of your calculations as floating-point. For example, to produce values in a range from 0 to limit, you could use the following statements:

Because you’ve declared limit to be of type double, the compiler will promote the integer that’s returned by rand() to that type so you don’t need to insert an explicit cast.

This article is excerpted from Beginning ANSI C++ The Complete Language by Ivor Horton (Apress, 2004; ISBN 1590592271). Check it out at your favorite bookstore today. Buy this book now.