Basic Data Types and Calculations

This article looks at some of the basic data types that are built into C++. If you’re learning how to use C++, you will want to keep reading, since you’ll be using these data types in all of your programs. It is taken from chapter two of the book Beginning ANSI C++: The Complete Language, by Ivor Horton (Apress, 2004; ISBN: 1590592271).

IN THIS CHAPTER, you’ll look at some of the basic data types that are built into C++ and that you’re likely to use in all your programs. You’ll also investigate how to carry out simple numerical computations. All of C++’s object-oriented capability is founded on the basic data types built into the language, because all the data types that you’ll create are ultimately defined in terms of the basic types. It’s therefore important to get a good grasp of using them. By the end of the chapter, you’ll be able to write a simple C++ pro gram of the traditional form: input – process – output.

In this chapter, you’ll learn about

  • Data types in C++

     

  • What literals are and how you define them in a program

     

  • Binary and hexadecimal representation for integers

     

  • How you declare and initialize variables in your program

     

  • How calculations using integers work

     

  • Programming with values that aren’t integers—that is, floating-point calculations

     

  • How you can prevent the value stored in a variable from being modified

     

  • How to create variables that can store characters

     

Data and Data Types

C++ is a strongly typed language. In other words, every data item in your program has a type associated with it that defines what it is and your C++ compiler will make extensive checks to ensure that, as far as possible, you use the right data type in any given context and that when you combine different types, they’re made to be compatible. Because of this type checking, the compiler is able to detect and report most errors that would arise from the accidental interpretation of one type of data as another or from attempts to combine data items of types that are mutually incompatible.

The numeical values that you can work with in C++ fall into two broad categories: integers (in other words, whole numbers) and floating-point values, which can be fractional. You can’t conclude from this that there are just two numerical data types, however. There are actually several data types in each of these categories, and each type has its own permitted range of values that it can store. Before I get into numerical types in detail, let’s look at how you carry out arithmetic calculations in C++, starting with how you can calculate using integers.

{mospagebreak title=Performing Simple Calculations}

To begin with, let’s get some bits of terminology out of the way. An operation (such as a mathematical calculation) is defined by an operator— + for addition, for example, or * for multiplication. The values that an operator acts upon are called operands, so in an expression such as 2*3 , the operands are 2 and 3 .

+ for addition, for example, or * for multiplication. The values that an operator acts upon are called , so in an expression such as 2*3 , the operands are 2 and 3 . To begin with, let’s get some bits of terminology out of the way. An operation (such as a mathematical calculation) is defined by an — + for addition, for example, or * for multiplication. The values that an operator acts upon are called , so in an expression such as 2*3, the operands are 2 and 3.

Because the multiplication operator requires two operands, it is called a binary operator. Some other operators only require one operand, and these are called unary operators. An example of a unary operator is the minus sign in –2 . The minus sign acts on one operand—the value 2—and changes its sign. This contrasts with the binary subtraction operator in expressions such as 4 – 2 , which acts on two operands, the 4 and the 2 .

Introducing Literals

In C++, fixed values of any kind, such as 42 , or 2.71828 , or “Mark Twain” , are referred to as literals.In Chapter 1, when you were outputting text strings to the screen, you used a string literal—a constant defined by a series of characters between a pair of double quotes, of which “Mark Twain” is an example. Now you’ll investigate the types of literals that are numeric constants. These are the ordinary numbers you meet every day: your shoe size, the boiling point of lead, the number of angels that can sit on a pin—in fact, any defined number.

There are two broad classifications of numeric constants that you can use in C++:

  • Integer literals are whole numbers and are written without a decimal point.
  • Floating-point literals (commonly referred to as floating-point numbers) are numbers that can be nonintegral values and are always written with a decimal point, or an exponent, or both. (You’ll look into exponents a little later on.)

You use an integer when you’re dealing with what is evidently a whole number: the number of players on a team, for example, or the number of pages in a book. You use a floating-point value when the values aren’t integral: the circumference of a circle divided by its diameter, for example, or the exchange rate of the UK£ against the US$.

Floating-point numbers are particularly helpful when you’re dealing with very small or very large quantities: the weight of an electron, the diameter of the galaxy, or the velocity of a bat out of hell, perhaps. The term “floating-point number” is used because while these values are represented by a fixed number of digits, called the precision, the decimal point “floats” and can be moved in either direction in relation to the fixed set of digits.

Letting the Point Float

Look at these two numbers:


0.000000000000000000001234567                1.234567×10-21
123456700000000000000000000.0                1.234567×10+26


 

Both numbers have seven digits of precision, but they’re very different numbers, the first being an extremely small number and the second being very large. A floating-point representation of each number on the left is shown to its right. Multiplying the number by a power of 10 shifts the decimal point in the base number, 1.234567. This flexibility in positioning the decimal point allows a huge range of numbers to be represented and stored, from the very small to the very large, in a modest amount of memory.

You’ll look at how to use integers first, as they’re the simpler of the two. You’ll come back to working with floating-point values as soon as you’re done with integers.

Integer Literals

You can write integer literals in a very straightforward way. Here are some examples:

-123    +123     123      22333

Here, the + and - signs in the first two examples are examples of the unary operators I mentioned earlier. You could omit the + in the second example, as it’s implied by default, but if you think putting it in makes things clearer, that’s not a problem. The literal +123 is the same as 123 . The fourth example is the number that you would normally write as 22,333, but you must not use commas within an integer literal. If you include a comma, the compiler is likely to treat your number as two numbers separated by the comma.

You can’t write just any old integer value that you want, either. To take an extreme example, an integer with 100 digits won’t be accepted. There are upper and lower limits on integer literals, and these are determined by the amount of memory that’s devoted to storing each type of integer value on the computer that you’re using. I come back to this point a little later in the chapter when I discuss integer variables, and I also cover some further options for specifying integer literals.

Of course, although I’ve written the examples of integer literals as decimal values, inside your computer they’re stored as binary numbers. Understanding binary arithmetic is quite important in programming, so in case you’re a little rusty on how binary numbers work, I’ve included a brief overview in Appendix E. If you don’t feel comfortable with binary and hexadecimal numbers, I suggest you take a look at the overview in Appendix E before continuing with the next section.

Hexadecimal Integer Literals

The previous examples of integer literals were decimal integers, but you can also write integers as hexadecimal values. To indicate that you’re writing a hexadecimal value, you prefix the number with 0x or 0X , so if you write 0x999 , you’re writing a hexadecimal number with three hexadecimal digits. Plain old 999 , on the other hand, is a decimal value with decimal digits, so the value will be completely different. Here are some more examples of integer literals written as hexadecimal values:

 


 

Hexadecimal values 0x1AF 0x123 0xA 0xCAD 0xFF
Corresponding decimal expression 1*162 1*162 10*160 12*162 15*161
  +10*161 +2*161   +10*161 +15*160
  +15*160 +3*160   +13*160  
Decimal value 431 291 10 3245 255

 


 

You’ll remember that in Chapter 1 you saw hexadecimal notation being used in escape sequences that defined characters. What you’re looking at here is different— you’re defining integers. You’ll come back to defining character literals later in this chapter.

The major use for hexadecimal literals is when you want to define a particular pattern of bits. Because each hexadecimal digit corresponds to 4 bits in the binary value, it’s easy to express a particular pattern of bits as a hexadecimal literal. You’ll explore this further in the next chapter.

Octal Integer Literals

You can also write integers as octal values—that is, using base 8. You identify a number as octal by writing it with a leading zero. Here are some examples of octal values:

 


Octal values                   0123  077  010101
Corresponding decimal integers 83    63   4161


 

Of course, octal numbers can only have digit values from 0 to 7. Octal is used very infrequently these days, and it survives in C++ largely for historical reasons from the time when there were computers around with a word length that was a multiple of 3 bits. However, it’s important to be aware of the existence of octal numbers, because if you accidentally write a decimal number with a leading zero, the compiler will try to interpret it as octal.

CAUTION Don’t write decimal integer values with a leading zero. The compiler will interpret such values as octal (base 8), so a value written as 065 will be equivalent to 53 in decimal notation.

As far as your compiler is concerned, it doesn’t matter which number base you choose when you write an integer value—ultimately, it will be stored in your computer as a binary number. The different ways available to you for writing an integer are there just for your convenience. You could write the integer value fifteen as 15 , as 0xF , or as 017 . These will all result in the same internal binary representation of the value, so you will choose one or other of the possible representations to suit the context in which you are using it.

Integer Arithmetic

The basic arithmetic operations that you can carry out on integers are shown in Table 2-1.

Table 2-1. Basic Arithmetic Operations


Operator  Operation
   +      Addition
   –      Subtraction
   *      Multiplication
   /      Division
   %      Modulus (the remainder after division)

 

The operators in Table 2-1 work largely in the way you would expect, and notice that they are all binary operators. However, the division operation is slightly idiosyncratic, so let’s examine that in a little more detail. Because integer operations always produce an integer result, an expression such as 11/4 doesn’t result in a value of 2.75 . Instead, it produces 2 . Integer division returns the number of times that the denominator divides into the numerator. Any remainder is simply discarded. So far as the C++ standard is concerned, the result of division by zero is undefined, but specific implementations will usually have the behavior defined and, in some cases, will provide a programmatic means of responding to the situation, so check your product documentation.

Figure 2-1 illustrates the different effects of the division and modulus operators.

 
Figure 2-1.  Contrasting the division and modulus operators

The modulus operator, % , which is sometimes referred to as the remainder operator, complements the division operator in that it provides a means for you to obtain the remainder after integer division if you need it. The expression 11%4 results in the value 3 , which is the remainder after dividing 11 by 4. When either or both operands of the modulus operator are negative, the sign of the remainder is up to the particular C++ implementation you’re using, so beware of variations between different systems. Because applying the modulus operator inevitably involves a division, the result is undefined when the right operand is zero.

Let’s see the arithmetic operators in action in an example.

{mospagebreak title=Try It Out: Integer Arithmetic in Action}

The following is a program to output the results of a miscellaneous collection of expressions involving integers to illustrate how the arithmetic operators work: // Program 2.1 – Calculating with integer constants
#include <iostream>              // For output to the screen
using std::cout;
using std::endl;

int main() {
cout << 10 + 20            << endl; // Output is 30
cout << 10 – 5             << endl; // Output is 5
cout << 10 – 20            << endl; // Output is -10

cout << 10 * 20            << endl; // Output is 200
cout << 10/3               << endl; // Output is 3
cout << 10 % 3             << endl; // Output is 1
cout << 10 % -3            << endl; // Output is 1
cout << -10 % 3            << endl; // Output is -1
cout << -10 % -3           << endl; // Output is -1

cout << 10 + 20/10 – 5     << endl; // Output is 7
cout << (10 + 20)/(10 – 5) << endl; // Output is 6
cout << 10 + 20/(10 – 5)   << endl; // Output is 14
cout << (10 + 20)/10 – 5   << endl; // Output is -2

cout << 4*5/3%4 + 7/3      << endl; // Output is 4
return 0;                           // End the program
}

The output from this example on my system is as follows:

 


 

30
5
-10
200
3
1
1
-1
-1
7
6
14
-2
4

 


 

It doesn’t look particularly elegant with that “ragged right” arrangement, does it? This is a consequence of the way that integers are output by default. Very shortly, you’ll come back to find out how you can make it look prettier. First, though, let’s look at the interesting parts of this example.

HOW IT WORKS

Each statement evaluates an arithmetic expression and outputs the result to the screen, followed by a newline character that moves the cursor to the beginning of the next line. All the arithmetic expressions here are constant expressions, because their values can be completely determined by the compiler before the program executes.

The first five statements are straightforward, and the reasons why they produce the results they do should be obvious to you:

cout << 10 + 20 << endl; // Output is  30
cout << 10 – 5 << endl;  // Output is   5
cout << 10 – 20 << endl; // Output is -10
cout << 10 * 20 << endl; // Output is 200
cout << 10/3 << endl;    // Output is   3

Because integer operations always produce integer results, the expression 10/3 in the last line results in 3 , as 3 divides into 10 a maximum of three times. The remainder, 1, that is left after dividing by 3 is discarded.

The next four lines show the modulus operator in action:

cout << 10 % 3    << endl;   // Output is  1
cout << 10 % -3   << endl;   // Output is  1
cout << -10 % 3   << endl;   // Output is -1
cout << -10 % -3  << endl;   // Output is -1

Here you’re producing the remainder after division for all possible combinations for the signs of the operands. The output corresponding to the first line where both operands are positive is the only one guaranteed to be the same when you run it on your system. The results of the other three lines may have a different sign.

The next four statements show the effects of using parentheses:

cout << 10 + 20/10 – 5      << endl;   // Output is  7
cout << (10 + 20)/(10 – 5)  << endl;   // Output is  6
cout << 10 + 20 /(10 – 5)   << endl;   // Output is 14
cout << (10 + 20)/10 – 5    << endl;   // Output is -2

The parentheses override the “natural order” of execution of the operators in the expressions. The expressions within parentheses are always evaluated first, starting with the innermost pair if they’re nested and working through to the outermost.

In an expression involving several different operators, the order in which the operators are executed is determined by giving some operators priority over others. The priority assigned to an operator is called its precedence.With the operators for integer arithmetic that you’ve seen, the operators * , / , and % form a group that takes priority over the operators + and - , which form another group. You would say that each of the operators * , / , and % has a higher precedence than + and - . Operators within a given group— + and – , for example—have equal precedence. The last output statement in the example illustrates how prece dence determines the order in which the operators are executed:

cout << 4*5/3%4 + 7/3 << endl; // Output is 4

The + operator is of lower precedence than any of the others, so the addition will be performed last. This means that values for the two subexpressions, 4*5/3%4 and 7/3 , will be calculated first. The operators in the subexpression 4*5/3%4 are all of equal precedence, so the sequence in which these will be executed is determined by their associativity. The associativity of a group of operators can be either left or right. An operator that is left associative binds first to the operand on the left of the operator, so a sequence of such operators in an expression will be executed from left to right. Let’s illustrate this using the example.

In the expression 4*5/3%4 , each of the operators is left associative, which means that the left operand of each operator is whatever is to its left. Thus, the left operand for the multiplication operation is 4 , the left operand for the division operation is 4*5 , and the left operand for the modulus operation is 4*5/3 . The expression is therefore evaluated as ((4*5)/3)%4 , which, as I said, is left to right.

Although the associativity of the operators in an expression is involved in determining the sequence of execution of operators from the same group, it doesn’t say anything about the operands. For example, in the expression 4*5/3%4+7/3 , it isn’t defined whether the subexpression 4*5/3%4 is evaluated before 7/3 or vice versa. It could be either, depending on what your compiler decides. Your reaction to this might be “Who cares?” because it makes no difference to the result. Here, that’s true, but there are circumstances in which it can make a difference, and you’ll see some of them as you progress through this chapter.

Operator Precedence and Associativity

Nearly all operator groups are left associative in C++, so most expressions involving operators of equal precedence are evaluated from left to right. The only right associative operators are the unary operators, which I’ve already touched upon, and assignment operators, which you’ll meet later on.

You can put the precedence and associativity of the integer arithmetic operators into a little table that indicates the order of execution in an arithmetic expression, as shown in Table 2-2.

Table 2-2. The Precedence and Associativity of the Arithmetic Operators


Operators      Associativity
unary + –      Right
* / %          Left
+ –            Left

 

Each line in Table 2-2 is a group of operators of equal precedence. The groups are in sequence, with the highest precedence operators in the top line and the lowest precedence at the bottom. As it only contains three lines, this table is rather simplistic, but you’ll accumulate many more operators and add further lines to this table as you learn more about C++.

NOTE The C++ standard doesn’t define the precedence of the operators directly, but it can be determined from the syntax rules that are defined within the standard. In most instances it’s easier to work out how a given expression will execute from the operator precedence than from the syntax rules, so I’ll consider the precedence of each operator as I introduce it.

If you want to see the precedence table for all the operators in C++, you can find it in Appendix D.

{mospagebreak title=Try It Out: Fixing the Appearance of the Output}

Although it may not appear so, the output from the previous example is right justified. The “ragged right” appearance is due to the fact that the output for each integer is in a field width that’s exactly the correct number of characters to accommodate the value. You can make the output look tidier by setting the field width for each data item to a value of your choice, as follows:

Although it may not appear so, the output from the previous example is right justified. The “ragged right” appearance is due to the fact that the output for each integer is in a that’s exactly the correct number of characters to accommodate the value. You can make the output look tidier by setting the field width for each data item to a value of your choice, as follows:

// Program 2.1A – Producing neat output
#include // For output to the screen
#include // For manipulators
using std::cout;
using std::endl;
using std::setw;

int main()
{
cout << setw(10) << 10 + 20 << endl; // Output is  30
cout << setw(10) << 10 – 5  << endl; // Output is   5
cout << setw(10) << 10 – 20 << endl; // Output is -10

cout << setw(10) << 10 * 20 << endl; // Output is 200
cout << setw(10) << 10/3    << endl; // Output is   3
cout << setw(10) << 10 % 3  << endl; // Output is   1
cout << setw(10) << 10 % -3 << endl; // Output is   1
cout << setw(10) << -10 % 3 << endl; // Output is  -1
cout << setw(10) << -10 % -3<< endl; // Output is  -1
cout << setw(10) << 10 + 20/10 – 5     << endl; // Output is  7
cout << setw(10) << (10 + 20)/(10 – 5) << endl; // Output is  6

cout << setw(10) << 10 + 20/(10 – 5)   << endl; // Output is 14
cout << setw(10) << (10 + 20)/10 – 5   << endl; // Output is -2
cout << setw(10) << 4*5/3%4 + 7/3      << endl; // Output is  4
return 0;                               // End the program
}

Now the output looks like this:


 

       30 
        5
      -10
      200
        3
        1
        1
       -1
       -1
        7
        6
       14
       -2
        4
 

 


 

 

 

HOW IT WORKS

That’s much nicer, isn’t it? The tidy formatting is accomplished by the changes to the output statements. Each value to be displayed is preceded in the output by setw(10) , as in the first statement:

cout << setw(10) << 10 + 20     << endl; // Output is 30

setw() is called a manipulator because it enables you to manipulate, or control, the appearance of the output. A manipulator doesn’t output anything; it just modifies the output process. Its effect is to set the field width for the next value to be output to the number of characters that you specify between the parenthe ses, which is 10 in this case. The field width that you set by using setw() only applies to the next value that is written to cout . Subsequent values will be presented in the default manner.

The additional #include statement for the standard header <iomanip> is necessary to make the setw() manipulator available in your program. I’ve also added a using declaration so you can use the setw name unqualified. There are other manipulators that you’ll try out in other examples as you go along. Meanwhile, you can try out this example with different field widths to see their effect.

Using Variables

Calculating with integer constants is all very well, but you were undoubtedly expecting a bit more sophistication in your C++ programs than that. To do more, you need to be able to store data items in a program, and this facility is provided by variables. A variable is an area in memory that’s identified by a name that you supply and that you can use to store an item of data of a particular type. Specifying a variable therefore requires two things: you must give it a name, and you must identify what kind of data you propose to store in it. First of all, let’s consider what options you have for defining variable names.

Calculating with integer constants is all very well, but you were undoubtedly expecting a bit more sophistication in your C++ programs than that. To do more, you need to be able to store data items in a program, and this facility is provided by variables. A is an area in memory that’s identified by a name that you supply and that you can use to store an item of data of a particular type. Specifying a variable therefore requires two things: you must give it a name, and you must identify what kind of data you propose to store in it. First of all, let’s consider what options you have for defining variable names. Variable Names

As you saw in Chapter 1, the name that you give to a variable can consist of any combination of upper- or lowercase letters, underscores, and the digits 0 to 9, but it must begin with a letter or an underscore. As I said in Chapter 1, the ANSI standard says that a variable name can also include UCS characters, and although you could use this in defining your variable names, it’s there to allow compilers to accommodate the use of national language characters that aren’t in the basic set of upper- and lowercase letters ( A to Z).

Don’t forget, you must not express any character from the basic source character set as a UCS character. All characters from the basic source character set must appear as their explicit character representation.

You saw some examples of valid variable names in Chapter 1, but here are a few more:

value   monthlySalary   eight_ball   FIXED_VALUE   JimBob

Just to remind you of what I said in Chapter 1, a variable name can’t begin with a digit, so names such as 8ball and 7Up aren’t valid. Also, because C++ is a case-sensitive language, republican and Republican are different names. You shouldn’t use variable names that begin with an underscore followed by a capital letter or that contain two successive underscores, as names of these forms are reserved for use within the standard libraries.

Generally, the names that you invent for your variables should be indicative of the kind of data that they hold. For instance, a name such as shoe_size is going to mean a whole lot more than ss —always assuming you’re dealing with shoe sizes, of course. You’ll find that you often want to use names that combine two or more words to make your program more understandable. One common approach for doing this uses the underscore character to link words in a single, for example:

line_count   pay_rise   current_debt

A convention that’s frequently adopted in C++ is to reserve names that begin with a capital letter for naming classes, which are user-defined types. You’ll learn how to define your own data types in Chapter 11. With this approach to names, Point , Person , and Program are all immediately recognizable as user-defined types and not variables. Of course, you’re free to assign any names that you want (as long as they aren’t keywords), but if you choose names that are meaningful and name your variables in a consistent manner, it will make your programs more readable and less error-prone. Appendix B contains a list of all the C++ keywords.

Integer Variables

Suppose you want to use a variable to record how many apples you have. You can create a variable with the name apples by means of a declaration statement for the variable, as shown in Figure 2-2.

by means of a for the variable, as shown in Figure 2-2. apples by means of a for the variable, as shown in Figure 2-2. Suppose you want to use a variable to record how many apples you have. You can create a variable with the name apples by means of a for the variable, as shown in Figure 2-2.

 
Figure 2-2.   A variable declaration

The statement in Figure 2-2 is described as a declaration because it declares the name apples . Any statement that introduces a name into your program is a declaration for that name. The statement in the illustration is also called a definition, because it causes memory to be allocated for the variable apples . Later, you’ll meet statements that are declarations but are not definitions. A variable is created by its definition, so you can only refer to it after the definition statement. If you attempt to refer to a variable prior to its definition, you’ll get an error message from the compiler.

When you define a variable, you can also specify an initial value. For example,

int apples = 10; // Definition for the variable apples

defines the variable called apples and sets its initial value as 10 . The definition in the diagram had no initial value specified, so the memory assigned to the variable would contain whatever junk value was left over from previous use of the memory. Having junk values floating around in your program is a bad idea, and this leads to our first golden rule.

GOLDEN RULE Always initialize your variables when you define them. If you don’t know what value a variable should have when you define it, initialize it to zero.

You can use variables as operands of the arithmetic operators you’ve seen in exactly the same way as you’ve used literals. The value of the variable will be the operand value. If you apply the unary minus operator to a variable, the result is a value that has the opposite sign of the value of the variable, but the same magnitude. This doesn’t change the value stored in the variable, though. You’ll see how to do that very soon.

Let’s try out some integer variables in a little program.

{mospagebreak title=Try It Out: Using Integer Variables}

Here’s a program that figures out how your apples can be divided equally among a group of children: // Program 2.2 – Working with integer variables
#include <iostream>          // For output to the screen
using std::cout;
using std::endl;

int main() {
  int apples = 10;  // Definition for the variable apples
  int children = 3; // Definition for the variable children

  // Calculate fruit per child
  cout << endl               // Start on a new line
       << “Each child gets ” // Output some text
       << apples/children   // Output number of apples per child
       << ” fruit.”;         // Output some more text

  // Calculate number left over
  cout << endl              // Start on a new line
       << “We have ”        // Output some text
       << apples % children // Output apples left over
       << ” left over.”;    // Output some more text

  cout << endl;
  return 0;                 // End the program
}

I’ve been very liberal with the comments here, just to make it clear what’s going on in each statement. You wouldn’t normally put such self-evident information in the comments. This program produces the following output:


Each child gets 3 fruit.
We have 1 left over.


 

HOW IT WORKS

This example is unlikely to overtax your brain cells. The first two statements in main() define the variables apples and children :

int apples = 10;  // Definition for the variable apple s
int children = 3; // Definition for the variable children

The variable apples is initialized to 10, and children is initialized to 3. Had you wanted, you could have defined both variables in a single statement, for example:

int apples = 10, children = 3;

This statement declares both apples and children to be of type int and initializes them as before. A comma is used to separate the variables that you’re declaring, and the whole thing ends with a semicolon. Of course, it isn’t so easy to add explanatory comments here as there’s less space, but you could split the state ment over two lines:

int apples = 10,  // Definition for the variable apple s
    children = 3; // Definition for the variable children

A comma still separates the two variables, and now you have space for the comments at the end of each line. You can declare as many variables as you want in a single statement, and you can spread the statement over as many lines as you see fit. However, it’s considered good style to stick to one declaration per statement.

The next statement calculates how many apples each child gets when the apples are divided up and outputs the result:

cout << endl               // Start on a new line
     << “Each child gets ” // Output some text
     << apples/children    // Output number of apples per child
     << ” fruit.”;         // Output some more text

Notice that the four lines here make up a single statement, and that you put comments on each line that are therefore effectively in the middle of the state ment. The arithmetic expression uses the division operator to obtain the number of apples that each child gets. This expression just involves the two variables that you’ve defined, but in general you can mix variables and literals in an expression in any way that you want.

The next statement calculates and outputs the number of apples that are left over:

cout << endl               // Start on a new line
     << “We have ”         // Output some text
     << apples % children  // Output number of apples per child
     << ” left over.”;     // Output some more text

Here, you use the modulus operator to calculate the remainder, and the result is output between the text strings in a single output statement. If you wanted, you could have generated all of the output with a single statement. Alternatively, you could equally well have output each string and data value in a separate statement.

In this example, you used the int type for your variables, but there are other kinds of integer variables.

Integer Variable Types

The type of an integer variable will determine how much memory is allocated for it and, consequently, the range of values that you can store in it. Table 2-3 describes the four basic types of integer variables.

Table 2-3. Basic Integer Variable Types


Type Name    Typical Memory per Variable
char         1 byte
short int    2 bytes
int          4 bytes
long int     8 bytes


 

Apart from type char , which is always 1 byte, there are no standard amounts of memory for storing integer variables of the other three types in Table 2-3. The only thing required by the C++ standard is that each type in the sequence must occupy at least as much memory as its predecessor. I’ve shown the memory for the types on my system, and this is a common arrangement. The type short int is usually written in its abbreviated form, short , and the type long int is usually written simply as long . These abbreviations correspond to the original C type names, so they’re universally accepted by C++ compilers. At first sight, char might seem an odd name for an integer type, but because its primary use is to store an integer code that represents a character, it does make sense.

You’ve already seen how to declare a variable of type int , and you declare variables of type short int and type long int in exactly the same way. For example, you could define and initialize a variable called bean_count , of type short int , with the following statement:

short int bean_count = 5;

As I said, you could also write this as follows:

int bean_count = 5;

Similarly, you can declare a variable of type long int with this statement:

long int earth_diameter = 12756000L; // Diameter in meters

Notice that I appended an L to the initializing value, which indicates that it’s an integer literal of type long int . If you don’t put the L here, it won’t cause a problem. The compiler will automatically arrange for the value to be converted from type int to type long int . However, it’s good programming practice to make the types of your initializing values consistent with the types of your variables.

Signed and Unsigned Integer Types

Variables of type short int , type int , and type long int can store negative and positive values, so they’re implicitly signed integer types. If you want to be explicit about it, you can also write these types as signed short int , signed int , and signed long int , respectively. However, they’re most commonly written without using the signed keyword.

You may see just the keyword signed written by itself as a type, which means signed int . However, you don’t see this very often probably because int is fewer characters to type! Occasionally you’ll see the type unsigned int written simply as unsigned . Both of these abbreviations originate in C. My personal preference is to always specify the underlying type when using the keywords signed or unsigned , as then there’s no question about what is meant.

An unsigned integer variable can only store positive values, and you won’t be surprised to learn that the type names for three such types are unsigned short int , unsigned int , and unsigned long int . These types are useful when you know you’re only going to be dealing with positive values, but they’re more frequently used to store values that are viewed as bit patterns rather than numbers. You’ll see more about this in Chapter 3, when you look at the bitwise operators that you use to manipulate individual bits in a variable.

You need a way of differentiating unsigned integer literals from signed integer literals, if only because 65535 can be stored in 16 bits as an unsigned value, but as a signed value you have to go to 32 bits. Unsigned integer literals are identified by the letter U or u following the digits of the value. This applies to decimal, hexadecimal, and octal integer literals. If you want to specify a literal to be type unsigned long int , you use both the U or u and the L .

 
Figure 2-3.  Signed and unsigned integers

Figure 2-3 illustrates the difference between 16-bit signed and unsigned integers. As you’ve seen, with signed integers, the leftmost bit indicates the sign of the number. It will be 0 for a positive value and 1 for a negative value. For unsigned integers, all the bits can be treated as data bits. Because an unsigned number is always regarded as positive, there is no sign bit—the leftmost bit is just part of the number.

If you think that the binary value for –32768 looks strange, remember that negative values are normally represented in 2’s complement form. As you’ll see if you look in Appendix E, to convert a positive binary value to a negative binary value (or vice versa) in 2’s complement form, you just flip all the bits and then add 1. Of course, you can’t represent +32768 as a 16-bit signed integer, as the available range only runs from –32768 to +32767.

Signed and Unsigned Char Types

Values stored as type char may actually be signed or unsigned, depending on how your compiler chooses to implement the type, so it may be vary between different computers or even between different compilers on the same computer. If you want a single byte to store integer values rather than character codes, you should explicitly declare the variable as either type signed char or type unsigned char .

Note that although type char will be equivalent to either signed char or unsigned char in any given compiler context, all three are considered to be different types. Of course, the words char , short , int , long , signed , and unsigned are all keywords.

Integer Ranges

The basic unit of memory in C++ is a byte. As far as C++ is concerned, a byte has sufficient bits to contain any character in the basic character set used by your C++ compiler, but it is otherwise undefined. As long as a byte can accommodate at least 96 characters, then it’s fine according to the C++ standard. This implies that a byte in C++ is at least 7 bits, but it could be more, and 8-bit bytes seem to be popular at the moment at least. The intention here is to remove hardware architecture dependencies from the standard. If at some future date there’s a reason to produce machines with 16-bit bytes, for instance, then the C++ standard will accommodate that and will still apply. For the time being, though, you should be safe in assuming that a byte is 8 bits.

As I said earlier, the memory allocated for each type of integer variable isn’t stipulated exactly within the ANSI C++ standard. What is said on the topic is the following:

A variable of type char occupies sufficient memory to allow any character from the basic character set to be stored, which is 1 byte.

A value of type int will occupy the number of bytes that’s natural for the hardware environment in which the program is being compiled.

The signed and unsigned versions of a type will occupy the same amount of memory.

A value of type short int will occupy at least as many bytes as type char ; a value of type int will occupy at least as many bytes as type short int ; and a value of type long int will occupy at least as many bytes as type int .

In a sentence, type char is the smallest with 1 byte, type long is the largest, and type int is somewhere between the two but occupies the number of bytes best suited to your computer’s integer arithmetic capability. The reason for this vagueness is that the number of bytes used for type int on any given computer should correspond to that which results in the most efficient integer arithmetic. This will depend on the architecture of the machine. In most machines, it’s 4 bytes, but as the performance and architecture of computer hardware advances, there’s increasing potential for it to be 8 bytes.

The actual number of bytes allocated to each integer type by your compiler will determine the range of values that can be stored. Table 2-4 shows the ranges for some typical sizes of integer variables.

Table 2-4. Ranges of Values for Integer Variables


Type              Size (Bytes)  Range of Values
char              1             –128 to 127
unsigned char     1             0U to 255U
short             2             –32768 to 32767
unsigned short    2             0U to 65535U
int               4             –2147483648 to 2147483647
unsigned int      4             0U to 4294967295U
long              8             –9223372036854775808L to
                                9223372036854775807L
unsigned long     8             0 to 18446744073709551615UL


The Type of an Integer Literal

I’ve introduced the idea of prefixes being applied to an integer value to affect the number base for the value. I’ve also informally introduced the notion of the suffixes U and L being used to identify integers as being of an unsigned type or of type long . Let’s now pin these options down more precisely and understand how the compiler will determine the type of a given integer literal.

First, Table 2-5 presents a summary of the options you have for the prefix and suffix to an integer value.

Table 2-5. Suffixes and Prefixes for Integer Values


Suffix/Prefix           Description
No prefix               The value is a decimal number.
Prefix of 0x or 0X      The value is a hexadecimal number.
Prefix of 0 (a zero)    The value is an octal number.
Suffix of u or U        The value is of an unsigned type.
Suffix of L or l (lowercase L) The value is of type long.


 

The last two items in the table can be combined in any sequence or combination of upper- and lowercase U and L , so UL , LU , uL , Lu , and so on are all acceptable. Although you can use the suffix l , which is a lowercase L, you should avoid doing so because of the obvious potential for confusion with the digit 1.

Now let’s look at how the various combinations of prefixes and suffixes that you can use with integer literals will be interpreted by the compiler:

  • A decimal integer literal with no suffix will be interpreted as being of type int if it can be accommodated within the range of values provided by that type. Other wise, it will be interpreted as being of type long.

     

  • An octal or hexadecimal literal with no suffix will be interpreted as the first of the types int , unsigned int , long , and unsigned long in which the value can be accommodated.

     

  • A literal with a suffix of u or U will be interpreted as being of type unsigned int if the value can be accommodated within that type. Otherwise, it will be inter preted as type unsigned long.

     

  • A literal with a suffix of l or L will be interpreted as being of type long if the value can be accommodated within that type. Otherwise, it will be interpreted as type unsigned long.

     

  • A literal with a suffix combining both U and L in upper- or lowercase will be interpreted as being of type unsigned long.

     

If the value for a literal is outside the range of the possible types, then the behavior is undefined but will usually result in an error message from the compiler.

You’ll undoubtedly have noticed that you have no way of specifying an integer literal to be of type short int or unsigned short int . When you supply an initial value in a declaration for a variable of either of these types, the compiler will automatically convert the value of the literal to the required type, for example:

unsigned short n = 1000;

Here, according to the preceding rules, the literal will be interpreted as being of type int . The compiler will convert the value to type unsigned short and use that as the initial value for the variable. If you used -1000 as the initial value, this couldn’t be converted to type unsigned short because negative numbers are by definition outside the range of this type. This would undoubtedly result in an error message from the compiler.

Remember that the range of values that can be stored for each integer type is dependent on your compiler. Table 2-4 shows “typical” values, but your compiler may well allocate different amounts of memory for particular types, thus providing for different ranges of values. You also need to be conscious of the possible variations in types when porting an application from one system to another.

So far, I’ve largely ignored character literals and variables of type char . Because these have some unique characteristics, you’ll deal with character literals and variables that store character codes later in this chapter and press on with integer calculations first. In particular, you need to know how to store a result.

{mospagebreak title=The Assignment Operator}

You can store the result of a calculation in a variable using the assignment operator, = . Let’s look at an example. Suppose you declare three variables with the statements

= . Let’s look at an example. Suppose you declare three variables with the statements You can store the result of a calculation in a variable using the , =. Let’s look at an example. Suppose you declare three variables with the statements

int total_fruit = 0;
int apples = 10;
int oranges = 6;

You can calculate the total number of fruit with the statement

total_fruit = apples + oranges;

This statement will first calculate the value on the right side of the = , the sum of apples and oranges , and then store the result in the total_fruit variable that appears on the left side of the = .

It goes almost without saying that the expression on the right side of an assignment can be as complicated as you need. If you’ve defined variables called boys and girls that will contain the number of boys and the number of girls who are to share the fruit, you can calculate how many pieces of fruit each child will receive if you divide the total equally between them with the statement

int fruit_per_child = 0 ;
fruit_per_child = (apples + oranges) / (boys + girls);

Note that you could equally well have declared the variable fruit_per_child and initialized it with the result of the expression directly:

int fruit_per_child = (apples + oranges) / (boys + girls);

You can initialize a variable with any expression, as long as all the variables involved have already been defined in preceding statements.

Try It Out: Using the Assignment Operator

You can package some of the code fragments from the previous section into an executable program, just to see them in action:

// Program 2.3 – Using the assignment operator
#include <iostream>
using std::cout;
using std::endl;

int main() {
  int apples = 10;
  int oranges = 6;
  int boys = 3;
  int girls = 4;

  int fruit_per_child = (apples + oranges)/(boys + girls);

  scout << endl
        << “Each child gets ”
        << fruit_per_child << ” fruit.”;

  cout << endl;
  return 0;
}

This produces the following output:


Each child gets 2 fruit.


This is exactly what you would expect from the preceding discussion. Multiple Assignments

You can assign values to several variables in a single statement. For example, the fol lowing code sets the contents of apples and oranges to the same value:

apples = oranges = 10;

The assignment operator is right associative, so this statement executes by first storing the value 10 in oranges and then storing the value in oranges in apples , so it is effectively

apples = (oranges = 10);

This implies that the expression (oranges = 10) has a value—namely, the value stored in oranges , which is 10. This isn’t merely a curiosity. Occasions will arise in which it’s convenient to assign a value to a variable within an expression and then to use that value for some other purpose. You can write statements such as this:

fruit = (oranges = 10) + (apples = 11);

which will store 10 in oranges , 11 in apples , then add the two together and store the result in fruit . It illustrates that an assignment expression has a value. However, although you can write statements like this, I don’t recommend it. As a rule, you should limit the number of operations per statement. Always assume that one day another programmer will want to understand and modify your code. As such, it’s your job to promote clarity and avoid ambiguity.

Modifying the Value of a Variable

Because the assignment operation first evaluates the right side and then stores the result in the variable on the left, you can write statements like this:

apples = apples * 2;

This statement calculates the value of the right side, apples * 2 , using the current value of apples , and then stores the result back in the apples variable. The effect of the statement is therefore to double the value contained in apples .

The need to operate on the existing value of a variable comes up frequently—so much so, in fact, that C++ has a special form of the assignment operator to provide a shorthand way of expressing this.

The op= Assignment Operators

The op= assignment operators are so called because they’re composed of an operator and an equals sign (=). Using one such operator, the previous statement for doubling the value of apples could be written as follows:

apples *= 2;

This is exactly the same operation as the statement in the last section. The apples variable is multiplied by the value of the expression on the right side, and the result is stored back in apples . The right side can be any expression you like. For instance, you could write

apples *= oranges + 2;

This is equivalent to

apples = apples * (oranges + 2);

Here, the value stored in apples is multiplied by the number of oranges plus 2, and the result is stored back in apples . (Though why you would want to multiply apples and oranges together is beyond me!)

The op= form of assignment also works with the addition operator, so to increase the number of oranges by 2, you could write

oranges += 2;

This has the same effect as the same as the statement

oranges = oranges + 2;

You should be able to see a pattern emerging by now. You could write the general form of an assignment statement using the op= operator as lhs op= rhs; Here, lhs is a variable and rhs is an expression. This is equivalent to the statement

lhs = lhs op (rhs) ;

The parentheses around rhs mean that the expression rhs is evaluated first and th e result becomes the right operand for the operation op.

NOTE lhs is an lvalue , which is an entity to which you can assign a value. Lvalues are so called because they can appear on the left side of an assignment. The result of every expression in C++ will be either an lvalue or an rvalue .An rvalue is a result that isn’t an lvalue—that is, it can’t appear on the left of an assignment operation.

You can use a whole range of operators in the op= form of assignment. Table 2-6 shows the complete set, including some operators you’ll meet in the next chapter.

Table 2-6. op= Assignment Operators


Operation      Operator   Operation             Operator
Addition       +          Bitwise AND           &
Subtraction    –          Bitwise OR            |
Multiplication •          Bitwise exclusive OR  ^
Division       /          Shift left            <<
Modulus        %          Shift right           >>


Note that there can be no spaces between the operator and the = . If you include a space, it will be flagged as an error.

{mospagebreak title=Incrementing and Decrementing Integers}

You’ve seen how you can modify a variable using the assignment operator and how you can increment one with the += operator. I’m sure you’ve also deduced that you can decrement a variable with -= . However, there are two other rather unusual arithmetic operators that can perform the same tasks. They’re called the increment and decrement operators, ++ and – respectively.

+= operator. I’m sure you’ve also deduced that you can decrement a variable with -= . However, there are two other rather unusual arithmetic operators that can perform the same tasks. They’re called the and , ++ and — respectively. You’ve seen how you can modify a variable using the assignment operator and how you can increment one with the += operator. I’m sure you’ve also deduced that you can decrement a variable with -=. However, there are two other rather unusual arithmetic operators that can perform the same tasks. They’re called the and , ++ and –respectively.

These operators are more than just other options, and you’ll find them to be quite an asset once you get further into applying C++ in earnest. The increment and decrement operators are unary operators that can be applied to an integer variable. For example, assuming the variables are of type int , the following three statements all have exactly the same effect:

count = count + 1 ; count += 1; ++count;

The preceding statements each increment the variable count by 1. The last form, using the increment operator, is clearly the most concise. The action of this operator is different from the other operators that you’ve seen, in that it directly modifies the value of its operand. The effect in an expression is to increment the value of the variable and then to use that incremented value in the expression. For example, suppose count has the value 5, and you execute this statement:

total = ++count + 6;

The increment and decrement operators are of higher precedence than all the binary arithmetic operators. Thus, count will be first incremented to the value 6, and then this value will be used in the evaluation of the expression on the right side of the assign ment operation. The variable total will therefore be assigned the value 12.

You use the decrement operator in the same way as the increment operator:

total = –count + 6;

Assuming count is 6 before executing this statement, the decrement operator will reduce it to 5, and this value will be used to calculate the value to be stored in total , which will be 11.

Postfix Increment and Decrement Operations

So far, you’ve written the operators in front of the variables to which they apply. This is called the prefix form. The operators can also be written after the variables to which they apply; this is the postfix form, and the effect is slightly different. When you use the postfix form of ++ , the variable to which it applies is incremented after its value is used in context. For example, you can rewrite the earlier example as follows:

total = count++ + 6;

With the same initial value of 5 for count , total is assigned the value 11, because the initial value of count is used to evaluate the expression before the increment by 1 is applied. The variable count will then be incremented to 6. The preceding statement is equivalent to the following two statements:

total = count + 6 ; ++count;

In an expression such as a++ + b , or even a+++b , it’s less than obvious what is meant, or indeed what the compiler will do. These two expressions are actually the same, but in the second case you might really have meant a + ++b , which has a differ ent meaning—it evaluates to one more than the other two expressions. It would be clearer to write the preceding statement as follows:

total = 6 + count++;

Alternatively, you can use parentheses:

total = (count++) + 6;

The rules that I’ve discussed in relation to the increment operator also apply to the decrement operator. For example, suppose count has the initial value 5, and you write the statement

total = –count + 6;

This results in total having the value 10 assigned, whereas if you write the statement

total = 6 + count– ;

the value of total is set to 11.

You should avoid using the prefix form of these operators to operate on a variable more than once in an expression. Suppose the variable count has the value 5, and you write

total = ++count * 3 + ++count * 5;

First, it looks rather untidy, but that’s the least of the problems with this. Second, and crucially, the statement modifies the value of a variable more than once and the result is undefined in C++. You could and should get an error message from the compiler with this statement, but in some instances you won’t. This isn’t a desirable feature in a program to say the least, so don’t modify a variable more than once in a statement.

Note also that the effects of statements such as the following are undefined:

k = ++k + 1;

Here you’re incrementing the value of the variable that appears on the right of the assignment operator, so you’re attempting to modify the value of the variable k twice within one expression. Each variable can be modified only once as a result of evaluating a single expression, and the prior value of the variable may only be accessed to determine the value to be stored. Although such expressions are undefined according to the C++ standard, this doesn’t mean that your compiler won’t compile them. It just means that there is no guarantee of consistency in the results.

The increment and decrement operators are usually applied to integers, particularly in the context of loops, as you’ll see in Chapter 5, and you’ll see later in this chapter that they can be applied to floating-point values too. In later chapters, you’ll explore how they can also be applied to certain other data types in C++, in some cases with rather specialized (but very useful) effects.

The const Keyword

You’ll often feel the need to use constants of one kind or another in your programs: the number of days in January, perhaps, or p, the ratio of the circumference of a circle to its diameter, or even the number of buns in a baker’s dozen. However, you should avoid using numeric literals explicitly within calculations; it’s much better to use a variable that you’ve initialized to the appropriate value instead. For example, multiplying a value by 3 doesn’t necessarily communicate that you’re converting from yards to feet, but multiplying by a variable with the name feet_per_yard that you’ve initialized to the value 3 makes it absolutely clear what you’re doing. Explicit numeric literals in a program are sometimes referred to as magic numbers, particularly when their purpose and origin is less than obvious.

that you’ve initialized to the value 3 makes it absolutely clear what you’re doing. Explicit numeric literals in a program are sometimes referred to as , particularly when their purpose and origin is less than obvious. feet_per_yard that you’ve initialized to the value 3 makes it absolutely clear what you’re doing. Explicit numeric literals in a program are sometimes referred to as , particularly when their purpose and origin is less than obvious. You’ll often feel the need to use constants of one kind or another in your programs: the number of days in January, perhaps, or p, the ratio of the circumference of a circle to its diameter, or even the number of buns in a baker’s dozen. However, you should avoid using numeric literals explicitly within calculations; it’s much better to use a variable that you’ve initialized to the appropriate value instead. For example, multiplying a value by 3 doesn’t necessarily communicate that you’re converting from yards to feet, but multiplying by a variable with the name feet_per_yard that you’ve initialized to the value 3 makes it absolutely clear what you’re doing. Explicit numeric literals in a program are sometimes referred to as , particularly when their purpose and origin is less than obvious.

Another good reason for using a variable instead of a magic number is that you reduce the number of maintenance points in your code. Imagine that your magic number represents something that changes from time to time—an interest rate, for instance—and that it crops up on several occasions in your code. When the rate changes, you could be faced with a sizable task to correct your program. If you’ve defined a variable for the purpose, you only need to change the value once, at the point of initialization.

Of course, if you use a variable to hold a constant of this kind, you really want to nail the value down and protect it from accidental modifications. You can use the keyword const to do this, for example:

const int feet_per_yard = 3; // Conversion factor yards to feet

You can declare any kind of “variable” as const . The compiler will check that you don’t attempt to alter the value of such a variable. For example, if you put something const on the left of an assignment operator, it will be flagged as an error. The obvious consequence of this is that you must always supply an initial value for a variable that you declare as const .

Be aware that declaring a variable as const alters its type. A variable of type const int is quite different from a variable of type int .

Try It Out: Using const

You could implement a little program to convert a length entered as yards, feet, and inches into inches:

// Program 2.4 – Using const
#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main() {
  const int inches_per_foot = 12;
  const int feet_per_yard = 3;
  int yards = 0;
  int feet = 0;
  int inches = 0;

  // Read the length from the keyboar d
  cout << “Enter a length as yards, feet, and inches: “;
  cin >> yards >> feet >> inches;

  // Output the length in inches
  cout << endl
       << “Length in inches is ”
       << inches + inches_per_foot * (feet + feet_per_yard * yards)
       << endl;
  return 0;
}

A typical result from this program is the following:


Enter a length as yards, feet, and inches: 2 2 11
Length in inches is 107

HOW IT WORKS

There’s an extra using statement compared to previous examples:

using std::cin;

This introduces the name cin from the std namespace into the program file that refers to the standard input stream—the keyboard.

You have two conversion constants defined by the statements

const int inches_per_foot = 12;
const int feet_per_yard = 3;

Declaring them with the keyword const will prevent direct modification of these variables. You could test this by adding a statement such as

inches_per_foot = 15;

With a statement like this after the declaration of the constant, the program would no longer compile.

You prompt for input and read the values for yards , feet , and inches with these statements:

cout << “Enter a length as yards, feet, and inches: “;
cin >> yards >> feet >> inches;

Notice how the second line specifies several successive input operations from the stream, cin .You do this by using the extraction operator, >> , that I mentioned briefly in the last chapter. It’s analogous to using cout , the stream output opera tion, for multiple values. The appearance of the insertion and extraction operators provides you with a visual cue as to the direction in which data flows.

The first value read from the keyboard will be stored in yards , the second in feet , and the third in inches . The input handling here is very flexible: you can enter all three values on one line, separated by spaces (in fact, by any whitespace characters), or you can enter them on several lines.

You perform the conversion to inches within the output statement itself:

cout << end l
     << “Length in inches is “
     << inches + inches_per_foot * (feet + feet_per_yard * yards)
     << endl;

As you can see, the fact that your conversion factors were declared as const in no way affects their use in expressions, just as long as you don’t try to modify them.

{mospagebreak title=Numerical Functions for Integers}

I explain functions in detail in Chapter 8, but that won’t stop us from making use of a few from the standard library before that. Let’s do a quick reprise of what’s going on when you use functions and cover some of the terminology they introduce. A function is a named, self-contained block of code that carries out a specific task. Often, this will involve it performing some operation on data that you supply and then returning the result of that operation to your program. In those circumstances in which a function returns a value that is numeric, the function can participate in an arithmetic expression just like an ordinary variable. In general, a call to a function looks like this:

FunctionName(argument1, argument2, … )

Depending on the function in question, you can supply zero, one, or more values for it to work with by placing them in parentheses after its name when you call it from your program. The values you pass to the function in this way are called arguments. Like all values in C++, the arguments you pass to a function, and the value it returns to your program, have types that you must take care to conform with in order to use the function.

You can access some numerical functions that you can apply to integers by adding an #include directive for the header at the beginning of your source file. This header has the same contents as the original C library header, stdlib.h . You’ll learn how each of these functions works by considering a few sample statements.

The abs() function returns the absolute value of the argument, which can be of type int or type long . The absolute value of a number is just its magnitude, so taking the absolute value of a negative number returns the number with a positive sign, whereas a positive number will be returned unchanged. The value returned by the abs() function will be of the same type as the argument, for example:

int value = -20;
int result = std::abs(value); // Result is 20

The <sctdlib> header also defines the labs() function that will also produce the absolute value of an argument of type long . This function is there because older C programs may use it, but I suggest you just use the abs() function.

The div() function takes two arguments, both of type int . It returns the result of dividing the first argument by the second as well as the remainder from the operation in the form of a structure of type div_t . I go into structures in detail later on, so for the moment you’ll just see how to access the quotient and the remainder from what is returned by the div() function through an example:

int value = 93;
int divisor = 17;
div_t results = std::div(value, divisor);       // Call the function
std::cout << “nQuotient is ” << results.quot;  // Quotient is 5
std::cout << “n Remainder is ” << results.rem; // Remainder is 8

The first two statements define the variables value and divisor and give them the initial values 93 and 17, respectively. The next statement calls the div() function to divide value by divisor . You store the resulting structure of type div_t that is returned by the function in the variable results , which is also of type div_t . In the first output statement, you access the quotient from results by appending the name quot to results , separated by a period. The period is called the member access operator, and here you’re using it to access the quot member of the results structure. Similarly, in the last statement you use the member access operator to output the value of the remain der, which is available from the rem member of the results structure. Any structure of type div_t will have members with the names quot and rem , and you always access them by using the member access operator.

Note that you could have used literals directly as arguments to the div() function. In this case, the statement calling the function would be

div_t results = std::div(93, 17);

The ldiv() function performs the same operation as the div() function, but on arguments of type long . The result is returned as a structure of type ldiv_t , which has members quot and rem that are of type long .

CAUTION The header is inherited from C, so many implementations will have definitions of the original C functions defined outside of the std name-space so the function names can be used without the std qualifier. This is to allow C programs to be compiled and executed in the same environment, but you should use the functions with the std qualifier because your code is C++.

Generating Random Numbers

Being able to generate random numbers in a program is very useful. You need to be able to build randomness into game programs, for instance; otherwise, they become very boring very quickly. The header defines a function rand() that will generate random integers. To be more precise, it generates pseudo-random integers. Random numbers by definition aren’t predictable, so any sequence of numbers produced by a numerical algorithm can’t be truly random. It just has the appearance of being so. However, now that you understand that, you’ll just refer to the numbers produced by the rand() function as random numbers. Note that rand() isn’t an outstanding random number generator. For many applications, you’ll probably want to use something that’s rather more sophisticated.

The rand() function return a random integer as type int . The function doesn’t require any arguments, so you can just use it like this:

int random_value = std::rand(); // A random integer

You store the integer that’s returned by the rand() function here in the variable random_value , but you could equally well use it in an arithmetic expression, for example:

int even = 2*std::rand();

The value returned by the rand() function will be a value that is from 0 to RAND_MAX . RAND_MAX is a symbol that is defined in . When you use RAND_MAX in your code, the compiler will replace it with an integer value. On my system it represents the value 0x7fff , but on other systems it may have a different value. It can be up to 0x3fffffff , which is the maximum integer you can store as type int . If this was the case, then you couldn’t multiply the value produced by rand() by 2, as you did previously, without running the risk of getting an incorrect result. You’ll see how you can get around this in the next chapter.

Because RAND_MAX is defined by a preprocessing macro (you’ll learn what a preprocessing macro is in Chapter 10), it isn’t within the std namespace, so you don’t need to qualify the name when you use it. Any symbol that’s defined by a macro won’t be in the std namespace because it isn’t a name that refers to something. By the time the compiler gets to compile the code, such a symbol will no longer be present because it will have already been replaced by something else during the preprocessing phase.

Making the Sequence Start at Random

Using rand() as you have so far, the sequence of numbers will always be the same. This is because the function uses a default seed value in the algorithm that generates the random numbers. This is fine for testing, but once you have a working game program, you’ll really want different sequences each time the program runs. You can change the seed value that will be used to generate the numbers by passing a new seed value as an integer argument to the srand() function that is defined in , for example:

std::srand(13); // Set seed for rand to 13

The argument to the srand() function must be a value of type unsigned int . Although the preceding statement will result in rand() generating a different sequence from the default, you really need a random seed to get a different sequence from rand() each time you execute a program. Fortunately, the clock on your computer provides a ready-made source of random seed values.

The standard library header defines several functions relating to the data and the time. You’ll just look at the time() function here because that’s precisely what you need to obtain a more or less random seed value. The time() function returns a value that’s the number of seconds that have elapsed since midnight on January 1, 1970, so if you use that as a seed, you can be certain that a program will use a different seed value each time it executes. The value is returned as type time_t , which is a type defined in the standard library to be equivalent to an integer type, usually type long . The return type is specified as type time_t to allow flexibility in the type of the return value in different C++ implementations. You can use the time() function to create the seed for a random number sequence like this:

std::srand((unsigned int)std::time(0));

There are a few things that you’ll have to take on trust for the moment. The argument to the time() function here is 0 . There’s another possibility for the argument, but you don’t need it here so you’ll ignore it. The subexpression (unsigned int) serves to convert the value returned by the time() function to type unsigned int , which is the type required for the argument to the srand() method. Without this, the statement wouldn’t compile. Type conversion is something else that you’ll look into later.

Let’s put a working example together that makes use of random number generation.

Try It Out: Generating Random Integers

Here’s the code:

  // Program 2.5 Using Random Integer s #include #include #include <iostream>
#include <cstdlib>
#include <ctime>

using std::cout;
using std::endl;
using std::rand;
using std::srand;
using std::time;

int main() {
  const int limit1 = 500; // Upper limit for on set of random values
  const int limit2 = 31; // Upper limit for another set of values

  cout << “First we will use the default sequence from rand().n”; 
  cout << “Three random integer from 0 to ” << RAND_MAX << “: ”
       << rand() << ” ” << rand() << ” ” << rand()<< endl;

  cout << endl << “Now we will use a new seed for rand().n”;
  srand((unsigned int)time(0)); // Set a new seed

  cout << “Three random integer from 0 to ” << RAND_MAX << “: “
       << rand() << ” ” << rand() << ” ” << rand()<< endl;
  return 0;
}

On my system I get the following output:


First we will use the default sequence from rand().
Three random integer from 0 to 32767: 6334 18467 41

 

Now we will use a new seed for rand().
Three random integer from 0 to 32767: 4610 32532 28452


 

HOW IT WORKS

This is a straightforward use of the rand() function, first with the default seed to start the sequence:

cout << “A random integer from 0 to ” << RAND_MAX << “: ”
     << rand() << endl;

Each call to rand() returns a value that will be from 0 to RAND_MAX , and you call the function three times to get a sequence of three random integers.

Next, you set the seed value as the current value of the system clock with this statement:

srand((unsigned int)time(0)); // Set a new seed

This statement will generally result in a different seed being set each time you execute the program. You then repeat the statement that you executed previous ly with the default seed set. Thus, each time you run this program, the first set will always produce the same output, whereas with the second set, the output should be different.

{mospagebreak title=Floating-Point Operations}

Numerical values that aren’t integers are stored as floating-point numbers. Internally, floating-point numbers have three parts: a sign (positive or negative), a mantissa (which is a value greater than or equal to 1 and less than 2 that has a fixed number of digits), and an exponent. Inside your computer, of course, both the mantissa and the exponent are binary values, but for the purposes of explaining how floating-point numbers work, I’ll talk about them as decimal values.

Numerical values that aren’t integers are stored as numbers. Internally, floating-point numbers have three parts: a sign (positive or negative), a mantissa (which is a value greater than or equal to 1 and less than 2 that has a fixed number of digits), and an exponent. Inside your computer, of course, both the mantissa and the exponent are binary values, but for the purposes of explaining how floating-point numbers work, I’ll talk about them as decimal values.

The value of a floating-point number is the signed value of the mantissa, multiplied by 10 to the power of the exponent, as shown in Table 2-7.

Table 2-7. Floating-Point Number Value


Sign(+/-)  Mantissa  Exponent           Value
    –       1.2345      3    –1.2345×103 (which is –1234.5)


 

You can write a floating point literal in three basic forms:

  • As a decimal value including a decimal point (for example, 110.0).

     

  • With an exponent (for example, 11E1) in which the decimal part is multiplied by the power of 10 specified after the E (for exponent). You have the option of using either an upper- or a lowercase letter E to precede the exponent.

     

  • Using both a decimal point and an exponent (for example, 1.1E2).

     

All three examples correspond to the same value, 110.0. Note that spaces aren’t allowed within floating-point literals, so you must not write 1.1 E2, for example. The latter would be interpreted by the compiler as two separate things: the floating-point literal 1.1 and the name E2.

NOTE A floating-point literal must contain a decimal point, or an exponent, or both. If you write a numeric literal with neither, then you have an integer.

Floating-Point Data Types

There are three floating-point data types that you can use, as described in Table 2-8.

Table 2-8. Floating-Point Data Types

Data Type Description
float Single precision floating-point values
double Double precision floating-point values
long double Double-extended precision floating-point values

The term “precision” here refers to the number of significant digits in the mantissa. The data types are in order of increasing precision, with float providing the lowest number of digits in the mantissa and long double the highest. Note that the precision only determines the number of digits in the mantissa. The range of numbers that can be represented by a particular type is determined by the range of possible exponents.

The precision and range of values aren’t prescribed by the ANSI standard for C++, so what you get with each of these types depends on your compiler. This will usually make the best of the floating-point hardware facilities provided by your computer. Generally, type long double will provide a precision that’s greater than or equal to that of type double , which in turn will provide a precision that is greater than or equal to that of type float .

Typically, you’ll find that type float will provide 7 digits precision, type double will provide 15 digits precision, and type long double will provide 19 digits precision, although double and long double turn out to be the same with some compilers. As well as increased precision, you’ll usually get an increased range of values with types double and long double .

Typical ranges of values that you can represent with the floating-point types on an Intel processor are shown in Table 2-9.

Table 2-9. Floating-Point Type Ranges

Type Precision (Decimal Digits) Range (+ or –)
float 7 1.2×10-38 to 3.4×1038
double 15 2.2×10-308 to 1.8×10308
long double 19 3.3×10-4932 to 1.2×104932

The numbers of decimal digits of precision in Table 2-9 are approximate. Zero can be represented exactly for each of these types, but values between zero and the lower limit in the positive or negative range can’t be represented, so these lower limits for the ranges are the smallest nonzero values that you can have.

Simple floating-point literals with just a decimal point are of type double , so let’s look at how to define variables of that type first. You can specify a floating-point variable using the keyword double , as in this statement:

double inches_to_mm = 25.4;

This declares the variable inches_to_mm to be of type double and initializes it with the value 25.4. You can also use const when declaring floating-point variables, and this is a case in which you could sensibly do so. If you want to fix the value of the variable, the declaration statement might be

const double inches_to_mm = 25.4; // A constant conversion factor

If you don’t need the precision and range of values that variables of type double provide you can opt to use the keyword float to declare your floating-point variable, for example:

float pi = 3.14159f;

This statement defines a variable pi with the initial value 3.14159. The f at the end of the literal specifies it to be a float type. Without the f , the literal would have been of type double , which wouldn’t cause a problem in this case, although you may get a warning message from your compiler. You can also use an uppercase letter F to indicate that a floating-point literal is of type float .

To specify a literal of type long double , you append an upper- or lowercase L to the number. You could therefore declare and initialize a variable of this type with the statement

long double root2 = 1.4142135623730950488L; // Square root of 2

Floating-Point Operations

The modulus operator, % , can’t be used with floating-point operands, but all the other binary arithmetic operators that you have seen, + , - , * , and / , can be. You can also apply the prefix and postfix increment and decrement operators, ++ and – , to a floating-point variable with the same effect as for an integer—the variable will be incremented or decremented by 1.

As with integer operands, the result of division by zero is undefined so far as the standard is concerned, but specific C++ implementations generally have their own way of dealing with this, so consult your product documentation.

With most computers today, the hardware floating-point operations are implemented according to the IEEE 754 standard (also known as IEC 559). Although IEEE 754 isn’t required by the C++ standard, it does provide for identification of some aspects of floating-point operations on machines on which IEEE 754 applies. The float-ing-point standard defines special values having a binary mantissa of all zeros and an exponent of all ones to represent +infinity or -infinity , depending on the sign. When you divide a positive nonzero value by zero, the result will be +infinity , and dividing a negative value by zero will result in -infinity . Another special floating-point value defined by IEEE 754 is called Not a Number, usually abbreviated to NaN . This is used to represent a result that isn’t mathematically defined, such as arises when you divide zero by zero or you divide infinity by infinity.

Any subsequent operation in which either or both operands are a value of NaN results in NaN . Once an operation in your program results in a value of ±infinity , this will pollute all subsequent operations in which it participates. Combining a normal value with ±infinity results in ±infinity . Dividing ±infinity by ±infinity or multiplying ±infinity by zero results in NaN . Table 2-10 summarizes all these possibilities.

Table 2-10. Floating-Point Operations with NaN Operands

Operation Result Operation Result
±N/0 ±Infinity 0/0 NaN
±Infinity±N ±Infinity ±Infinity/±Infinity NaN
±Infinity*N ±Infinity Infinity-Infinity NaN
±Infinity/N ±Infinity Infinity*0 NaN

Using floating-point variables is really quite straightforward, but there’s no substitute for experience, so let’s try an example.

{mospagebreak title=Try It Out: Floating-Point Arithmetic}

Suppose that you want to construct a circular pond in which you want to keep a number of fish. Having looked into the matter, you know that you must allow 2 square feet of surface area on the pond for every 6 inches of fish length. You need to figure out the diameter of the pond that will keep the fish happy. Here’s how you can do it:

// Program 2.6 Sizing a pond for happy fis h #include #include // For square root calculation using std::cout; using std::cin; using std::sqrt;

int main() { const double fish_factor = 2.0/0.5; // Area per unit length of fish const double inches_per_foot = 12.0; const double pi = 3.14159265;

double fish_count = 0.0; // Number of fish double fish_length = 0.0; // Average length of fish

cout << “Enter the number of fish you want to keep: “; cin >> fish_count; cout << “Enter the average fish length in inches: “; cin >> fish_length; fish_length = fish_length/inches_per_foot; // Convert to feet

// Calculate the required surface area double pond_area = fish_count * fish_length * fish_factor;

// Calculate the pond diameter from the area double pond_diameter = 2.0 * sqrt(pond_area/pi);

cout << “nPond diameter required for ” << fish_count << ” fish is ” << pond_diameter << ” feet.n”; return 0; }

(Continued)

With input values of 20 fish with an average length of 9 inches, this example produces the following output:

Enter the number of fish you want to keep: 20 Enter the average fish length in inches: 9 Pond diameter required for 20 fish is 8.74039 feet.

You first declare three const variables that you’ll use in the calculation:

const double fish_factor = 2.0/0.5; // Area per unit length of fish const double inches_per_foot = 12.0; const double pi = 3.14159265;

Notice the use of a constant expression to specify the value for fish_factor . You can use any expression that produces a result of the appropriate type to define an initializing value for a variable. You have declared fish_factor , inches_per_foot , and pi as const because you don’t want to allow them to be altered.

Next, you declare variables in which you’ll store the user input:

double fish_count = 0.0; // Number of fish double fish_length = 0.0; // Average length of fish

You don’t have to initialize these, but it’s good practice to do so.

Because the input for the fish length is in inches, you need to convert it to feet before you use it in the calculation for the pond:

fish_length = fish_length/inches_per_foot; // Convert to feet

This stores the converted value back in the original variable.

You get the required area for the pond with the following statement:

double pond_area = fish_count * fish_length * fish_factor;

The product of fish_count and fish_length gives the total length of all the fish, and multiplying this by fish_factor gives the required area.

The area of any circle is given by the formula p r2, where r is the radius. You can therefore calculate the radius of the pond as the square root of the area divided by p. The diameter is then twice the radius, and the whole calculation is carried out by this statement:

pond_diameter = 2.0 * sqrt(pond_area / pi);

74 You obtain the square root using a function that’s declared in the standard header . The sqrt() function returns the square root of the value of the expres sion placed between the parentheses after the function name. In this case, the value returned is of type double because the value of the expression is of type double , but there’s also a version that returns the square root of a float value as type float . The header contains declarations for many other standard library numerical functions, as you’ll see a little later in this chapter. You’ll look into functions in detail, including how you can define your own functions, in Chapter 8. Of course, like almost all the other names in the standard library, sqrt is defined within the std namespace, so you have a using declaration for the name at the beginning of your program file.

The last step before exiting main() is to output the result:

cout << “nPond diameter required for ” << fish_count << ” fish is ” << pond_diameter << ” feet.n”;

This outputs the pond diameter required for the number of fish specified. Working with Floating-Point Values

For most computations using floating-point values, you’ll find that type double is more than adequate. However, you need to be aware of the limitations and pitfalls of work ing with floating-point variables. If you’re not careful, your results may be inaccurate, or even incorrect. The following are common sources of errors when using floating-point values:

  • Many decimal values don’t convert exactly to binary floating-point values. The small errors that occur can easily be amplified in your calculations to produce large errors.

     

  • Taking the difference between two nearly identical values can lose precision. If you take the difference between two values of type float that differ in the sixth significant digit, you’ll produce a result that may have only one or two digits of accuracy. The other significant digits that are stored may represent errors.

     

  • Dealing with a wide range of possible values can lead to errors. You can create an elementary illustration of this by adding two values stored as type float with 7 digits of precision but in which one value is 108 times larger that the other. You can add the smaller value to the larger as many times as you like, and the larger value will be unchanged. The header defines constants for the floating-point types that are the smallest values that you can add to 1.0 and get a different result. The constants are FLT_EPSILON , DBL_EPSILON , and LDBL_EPSILON .

     

Let’s see how these errors can manifest themselves in practice, albeit in a somewhat artificial situation.

Try It Out: Errors in Floating-Point Calculations

Here’s an example contrived to illustrate how the first two points can combine to produce errors:

// Program 2.7 Floating point errors

#include
using std::cout;
using std::endl;
int main() {
float value1 = 0.1f;
float value2 = 2.1f;
value1 -= 0.09f; // Should be 0.01
value2 -= 2.09f; // Should be 0.01
cout << value1 – value2 << endl; // Should output zero
return 0;  
}  

The value displayed should be zero, but on my computer this program produces the following:

7.45058e-009

The reason for the error is that none of the numerical values is stored exactly. If you add code to output the values of value1 and value2 after they’ve been modified, you should see a discrepancy between them.

Of course, the final difference between the values of value1 and value2 is a very small number, but you could be using this totally spurious value in other calculations in which the error could be amplified. If you multiply this result by 1010, say, you’ll get an answer around 7.45, when the result should really be zero. Similarly, if you compare these two values, expecting them to be equal, you don’t get the result you expect.

CAUTION Never rely on an exact floating-point representation of a decimal value in your program code.

Tweaking the Output

The previous program outputs the floating-point value in a very sensible fashion. It gave you 5 decimal places, and it used scientific notation (that is, a mantissa and an exponent). However, you could have chosen to have the output displayed using “normal” decimal notation by employing some more output manipulators.

{mospagebreak title=Try It Out: Yet More Output Manipulators}

Here’s the same code as in the previous “Try It Out” exercise, except that it uses additional manipulators to improve the appearance of the output:

// Program 2.8 Experimenting with floating point output

#include #include using std::setprecision; using std::fixed; using std::scientific;

using std::cout; using std::endl;

int main() { float value1 = 0.1f; float value2 = 2.1f; value1 -= 0.09f; value2 -= 2.09f;

cout << setprecision(14) << fixed;

cout << value1 – value2 << endl;

cout << setprecision(5) << scientific; cout << value1 – value2 << endl;

return 0; } // Should be 0.01

// Should be 0.01

// Change to fixed notation

// Should output zero

// Set scientific notation // Should output zero

When I run the modified program, this is the output I get:

0.00000000745058 7.45058e-009

(Continued)

This code uses three new manipulators. The setprecision() manipulator specifies how many decimal places should appear after the decimal point when you’re outputting a floating-point number. The fixed and scientific manipulators complement one another and choose the format in which a floating-point number should be displayed when they’re written to the stream.

By default, your C++ compiler will select either scientific or fixed , depending on the particular value you’re outputting, and you saw in the first version of this program that it performed that task admirably. The default number of decimal places isn’t defined in the standard, but five is common.

Let’s look at the changes made. Apart from the #include for , just as you needed when you were using setw() earlier in the chapter and the additional using declarations, the interest is in these four lines of code:

cout << setprecision(14) << fixed; // Change to fixed notation
cout << value1 – value2 << endl; // Should output zero
cout << setprecision(5) << scientific; // Set scientific notation
cout << value1 – value2 << endl; // Should output zero

The first line is easy: you use the manipulators like you used setw() , by sending them to the output stream with the insertion operator. Their effects can then clearly be seen in the first line of output: you get a floating-point value with 14 decimal places and no exponent.

Note that these manipulators differ from setw() in that they’re modal.In other words, they remain in effect for the stream until the end of the program, unless you set a different option. That’s the reason for the third line in the preceding code—you have to set scientific mode and a precision of 5 explicitly in order to return to “default” behavior. You can see that you’ve succeeded, though, because the second line of output is the same as the one produced by the original program.

NOTE Actually, the header is only required here for the setprecision() manipulator. Both fixed and scientific are defined in . There are more manipulators to discuss, but the rule is that the ones requiring values (such as setw() and setprecision() ) are defined in , whereas the others are defined in .

Mathematical Functions

The standard library header defines a range of trigonometric and numerical functions that you can use in your programs. You’ve already seen the sqrt() function. Table 2-11 presents some other numerical functions from this header that you may find useful.

Table 2-11. Numerical Functions

Function

abs(arg)

fabs(arg) ceil(arg)

floor(arg)

exp(arg) log(arg) log10(arg) pow(arg1, arg2)

Description

Returns the absolute value of arg as the same type as arg , where ar g can be of any floating-point type. There are versions of the abs() function declared in the header file for arguments of typ e int and type long.

as the same type as arg , where ar g can be of any floating-point type. There are versions of the abs() function declared in the header file for arguments of typ e int and type long. arg as the same type as arg , where ar g can be of any floating-point type. There are versions of the abs() function declared in the header file for arguments of typ e int and type long. Returns the absolute value of arg as the same type as arg, where arg can be of any floating-point type. There are versions of the abs() function declared in the header file for arguments of type int and type long.

Returns the absolute value of arg as the same type as the argument.

The argument can be int , long , float , double , or long double. Returns a floating-point value of the same type as arg that is the smallest integer greater than or equal to arg , so ceil(2.5) produces the value 3.0. arg can be of any floating-point type.

Returns a floating-point value of the same type as arg that is the largest integer less than or equal to arg so the value returned by floor(2.5 ) will be 2.0. arg can be of any floating-point type .

Returns the value of e arg as the same type as arg . arg can be of any

floating-point type. The log function returns the natural logarithm (to base e) of arg as the same type as arg . arg can be any floating-point type.

The log10 function returns the logarithm to base 10 of arg as the same

type as arg . arg can be any floating-point type. The pow function returns the value of arg1 raised to the power arg1, which is arg1arg2. Thus the result of pow(2, 3) will be 8, and the result of pow(1.5, 3) will be 3.375. The arguments can be both of type int or any floating-point type. The second argument, arg2 , may also be of type int with arg1 of type int , or long , or any floating-point type. The value returned will be of the same type as arg1.

Table 2-12 shows the trigonometric functions that you have available in the header.

Table 2-12. Trigonometric Functions

Function Description
cos(angle) Returns the cosine of the angle expressed in radians that is passed as the argument.
sin(angle) Returns the sine of the angle expressed in radians that is passed as the
argument.
tan(angle) Returns the tangent of the angle expressed in radians that is passed as the argument.
cosh(angle) Returns the hyperbolic cosine of the angle expressed in radians that is passed as the argument. The hyperbolic cosine of a variable x is given by formula (ex-e-x)/2.
sinh(angle) Returns the hyperbolic sine of the angle expressed in radians that is passed as the argument. The hyperbolic sine of a variable x is given by the formula (ex+e-x)/2.
tanh(angle) Returns the hyperbolic tangent of the angle expressed in radians that is passed as the argument. The hyberbolic tangent of a variable x is given by the hyperbolic sine of x divided by the hyperbolic cosine of x.
acos(arg) Returns the inverse cosine (arccosine) of arg . The argument must be between –1 and +1. The result is in radians and will be from 0 to p.
asin(arg) Returns the inverse sine (arcsine) of the argument. The argument must be between –1 and +1. The result is in radians and will be from –p/2 to +p/2.
atan(arg) Returns the inverse tangent (arctangent) of the argument. The result is in radians and will be from –p/2 to +p/2.
atan2(arg1, arg2) This function requires two arguments of the same floating-point type. The function returns the inverse tangent of arg1/arg2 . The result will be in the range from –p to +p radians and of the same type as the
  arguments.

The arguments to these functions can be of any floating-point type and the result will be returned as the same type as the argument(s).

Let’s look at some examples of how these are used. Here’s how you can calculate the sine of an angle in radians:

double angle = 1.5; // In radians double sine_value = std::sin(angle);

If the angle is in degrees, you can calculate the tangent by using a value for p to convert to radians:

float angle_deg = 60.0f; // Angle in degree s const float pi = 1.14159f; const float pi_degrees = 180.0f; float tangent = std::tan(pi*angle_deg/pi_degrees);

If you know the height of the church steeple is 100 feet and you’re standing 50 feet from the base of the steeple, you can calculate the angle in radians of the top of the

steeple like this:
double height = 100.0; // Steeple height- feet
double distance = 50.0; // Distance from base
angle = std::atan2(height, distance); // Result in radians

You can use this value in angle and the value of distance to calculate the distance from your toe to the top of the steeple:

double toe_to_tip = distance*std::cos(angle);

Of course, fans of Pythagoras of Samos could obtain the result much more easily, like this:

double toe_to_tip = std::sqrt(std::pow(distance,2) + std::pow(height, 2);

{mospagebreak title=Working with Characters}

Variables of type char are primarily used to store a code for a single character and occupy 1 byte in memory. The C++ standard doesn’t specify the character encoding to be used for representing the basic character set, so this is determined by a particular compiler. It’s typically, but not exclusively, ASCII.

char are primarily used to store a code for a single character and occupy 1 byte in memory. The C++ standard doesn’t specify the character encoding to be used for representing the basic character set, so this is determined by a particular compiler. It’s typically, but not exclusively, ASCII. Variables of type char are primarily used to store a code for a single character and occupy 1 byte in memory. The C++ standard doesn’t specify the character encoding to be used for representing the basic character set, so this is determined by a particular compiler. It’s typically, but not exclusively, ASCII.

You declare variables of type char in the same way as variables of the other types that you’ve seen, for example:

char letter; char yes, no;

The first statement declares a single variable of type char with the name letter . The second variable declares two variables of type char having the names yes and no . Each of these variables can store the code for a single character. Because you haven’t pro vided initial values for these variables, they’ll contain junk values.

Character Literals

When you declare a variable of type char , you can initialize it with a character literal. You write a character literal as the character that you require between single quotes. For example, ‘z’ , ‘3’ , and ‘?’ are all character literals.

Some characters are problematical to enter as literals. Obviously, a single quote presents a bit of a difficulty because it’s a delimiter for a character literal. In fact, it isn’t legal in C++ to put either a single quote or a backslash character between single quotes. Control characters such as newline and tab are also a problem because they result in an effect when you press the key for the appropriate character rather than entering the character as data. You can specify all of these problem characters by using escape sequences that begin with a backslash, as shown in Table 2-13.

To specify a character literal corresponding to any of these characters, you just type in the corresponding escape sequence between single quotes. For instance, new-line is ‘n’ and backslash is ” .

There are also escape sequences that you can use to specify a character by its code expressed as either an octal or a hexadecimal value. The escape sequence for an octal character code is one to three octal digits preceded by a backslash. The escape sequence for a hexadecimal character code is one or more hexadecimal digits preceded by x . You write both forms between single quotes when you want to define a character literal. For example, the letter ‘A’ could be written as hexadecimal ‘x41′ or octal ’81’ in US-ASCII code. Obviously, you could write codes that won’t fit within a single byte, in which case the result is implementation defined.

If you write a character literal with more than one character between the single quotes and the characters don’t represent an escape sequence— ‘abc’ is an example— then the literal is described as a multicharacter literal and will be of type int . The numerical value of such a literal is implementation defined but will usually be the result of placing the 1-byte codes for the characters in successive bytes of the int value. If you specify a multicharacter literal with more than four characters, this will usually result in an error message from the compiler.

You now know enough about character literals to initialize your variables of type char properly.

Initializing char Variables

You can define and initialize a variable of type char with the statement

char letter = ‘A'; // Stores a single letter ‘A’

82 This statement defines the variable with the name letter to be of type char with an initial value ‘A’ . If your compiler represents characters using US-ASCII codes, this will have the decimal value 65.

You can declare and initialize multiple variables in a single statement:

char yes = ‘y’, no = ‘n’, tab = ‘t';

Because you can treat variables of type char as integers, you could equally well declare and initialize the variable letter with this statement:

char letter = 65; // Stores the ASCII code for ‘A’

Remember that type char may be signed or unsigned by default, depending on the compiler, so this will affect what numerical values can be accommodated. If char is unsigned, values can be from 0 to 255. If it’s signed, values can be from –128 to +127. Of course, the range of bit patterns that can be stored is the same in both cases. They’re just interpreted differently.

Of course, you can use the variable letter as an operand in integer operations, so you can write

letter += 2;

This will result in the value stored in letter being incremented to 67, which is ‘C’ in US-ASCII. You can find all the US-ASCII codes in Appendix A of this book.

CAUTION Although I’ve assumed US-ASCII coding in the examples, as I noted earlier although this is usually the case this doesn’t have to be so. On older main frame computers, for instance, characters may be represented using Extended Binary Coded Decimal Interchange Code (EBCDIC), in which the codes for some characters are different from US-ASCII.

You can explicitly declare a variable as type signed char or unsigned char , which will affect the range of integers that can be represented. For example, you can declare a variable as follows:

unsigned char ch = 0U;

In this case, the numerical values can range from 0 to 255.

When you read from a stream into a variable of type char , the first nonwhitespace character will be stored. This means that you can’t read whitespace characters in this way—they’re simply ignored. Further, you can’t read a numerical value into a variable of type char —if you try, you’ll find that the character code for the first digit will be stored. When you output a variable of type char to the screen, it will be as a character, not a numerical value. You can see this demonstrated in the next example.

Try It Out: Handling Character Values

This example reads a character from the keyboard, outputs the character and its numerical code, increments the value of the character, and outputs the result as a character and as an integer:

// Program 2.9 – Handling character value s #include using std::cin; using std::cout; using std::endl;

int main() { char ch = 0; int ch_value = 0;

// Read a character from the keyboard cout << “Enter a character: “; cin >> ch; ch_value = ch; // Get integer value of character

cout << endl << ch << ” is ” << ch_value;

ch_value = ++ch; // Increment ch and store as integer

cout << endl << ch << ” is ” << ch_value << endl;

return 0; }

Typical output from this example is as follows:

Enter a character: w

w is 119 x is 120

After prompting for input, the program reads a character from the keyboard with the statement

cin >> ch;

Only nonwhitespace characters are accepted, so you can press Enter or enter spaces and tabs and they’ll all be ignored.

Stream output will always output the variable ch as a character. To get the numer ical code, you need a way to convert it to an integer type. The next statement does this:

ch_value = ch; // Get integer value of character

The compiler will arrange to convert the value stored in ch from type char to type int so that it can be stored in the variable ch_value . You’ll see more about automatic conversions in the next chapter, when I discuss expressions involving values of different types.

Now you can output the character as well as its integer code with the following statement:

cout << end l << ch << ” is ” << ch_value;

The next statement demonstrates that you can operate with variables of type char as integers:

ch_value = ++ch; // Increment ch and store as integer

This statement increments the contents of ch and stores the result in the variable ch_value , so you have both the next character and its numerical representation. This is output to the display with exactly the same statement as was used previ ously. Although you just incremented ch here, variables of type char can be used with all of the arithmetic operators, just like any of the integer types.

Working with Extended Character Sets

Single-byte character codes such as ASCII or EBCDIC are generally adequate for national language character sets that use Latin characters. There are also 8-bit character encodings that will accommodate other languages such as Greek or Russian. However, if you want to work with these and Latin characters simultaneously, or if you want to handle character sets for Asian languages that require much larger numbers of character codes than the ASCII set, 256 character codes doesn’t go far enough.

The type wchar_t is a character type that can store all members of the largest extended character set that’s support by an implementation. The type name derives from wide characters, because the character is “wider” than the usual single-byte character. By contrast, type char is referred to as “narrow” because of the limited range of character codes that are available. The size of variables of type wchar_t isn’t stipulated by the C++ standard, except that it will have the same characteristics as one of the other integer types. It is often 2 bytes on PCs, and typically the underlying type is unsigned short , but it can also be 4 bytes with some compilers, especially those implemented on Unix workstations.

Wide-Character Literals

You define wide-character literals in the same way as narrow character literals that you use with type char , but you prefix them with the letter L. For example,

wchar_t wide_letter = L’Z';

defines the variable wide_letter to be of type wchar_t and initializes it to the wide-char-acter representation for Z.

Your keyboard may not have keys for representing other national language characters, but you can still create them using hexadecimal notation, for example:

wchar_t wide_letter = L’x0438′; // Cyrillic

The value between the single quotes is an escape sequence that allows you to specify a character by a hexadecimal representation of the character code. The backslash indicates the start of the escape sequence, and the x after the backslash signifies that the code is hexadecimal. The absence of x or X would indicate that the characters that follow are to be interpreted as octal digits.

Of course, you could also use the notation for UCS character literals:

wchar_t wide_letter = L’u0438′; // Cyrillic

If your compiler supports 4-byte UCS characters, you could also initialize a variable of type wchar_t with a UCS character specified as Udddddddd , where d is a hexadecimal digit.

Wide-Character Streams

The streams cin and cout that you’ve been using are narrow-character streams. They only handle characters that consist of a single byte, so you can’t extract from cin into a variable of type wchar_t . The header defines special wide-character streams, wcin and wcout for input and output of wide characters. You use the wide streams in the same way as the narrow streams. For instance, you can read a wide character from wcin like this:

wchat_t wide_letter = 0 ; std::wcin >> wide_letter; // Read a wide character

Although you’ll always be able to write wide characters to wcout , this doesn’t mean that such characters will display correctly or at all. It depends on if your operating sys tem recognizes the character codes.

{mospagebreak title=Functional Notation for Initial Values}

An alternative notation for specifying the initial value for a variable when you declare it is called functional notation. The term stems from the fact that you put the initial value between parentheses after the variable name, so it looks like a function call, as you’ll discover later on.

An alternative notation for specifying the initial value for a variable when you declare it is called . The term stems from the fact that you put the initial value between parentheses after the variable name, so it looks like a function call, as you’ll discover later on.

Let’s look at an example. Instead of writing a declaration as

int unlucky = 13;

you have the option to write the statement as

int unlucky(13);

Both statements achieve exactly the same result: they declare the variable unlucky as type int and give it an initial value of 13.

You can initialize other types of variables using functional notation. For instance, you could declare and initialize a variable to store a character with this statement:

char letter(‘A’);

However, functional notation for initializing variables is primarily used for the initialization of variables of a data type that you’ve defined. In this case, it really does involve calling a function. The initialization of variables of the fundamental types in C++ normally uses the approach you have taken up to now. You’ll have to wait until Chapter 11 to find out about creating your own types and how those kinds of variables get initialized!

Summary

In this chapter, I covered the basics of computation in C++. You learned about most of the fundamental types of data that are provided for in the language. The essentials of what I’ve discussed up to now are as follows:

  • Numeric and character constants are called literals.
  • You can define integer literals as decimal, hexadecimal, or octal values.
  • A floating-point literal must contain a decimal point, or an exponent, or both.
  • Named objects in C++, such as variables, can have names that consist of a sequence of letters and digits, the first of which is a letter, and where an underscore is considered to be a letter. Upper- and lowercase letters are distinguished.

     

  • Names that begin with an underscore followed by a capital letter, and names that contain two successive underscores, are reserved for use within the standard library, so you shouldn’t use them for names of your own variables.

     

  • All literals and variables in C++ are of a given type.

     

  • The basic types that can store integers are short , int , and long . These store signed integers by default, but you can also use the type modifier unsigned preceding any of these type names to produce a type that occupies the same number of bytes but only stores unsigned integers.

     

  • A variable of type char can store a single character and occupies 1 byte. The type char may be signed or unsigned by default, depending on your compiler. You can also use variables of the types signed char and unsigned char to store integers.

     

  • The type wchar_t can store a wide character and occupies either 2 or 4 bytes, depending on your compiler.

     

  • The floating-point data types are float , double , and long double .

     

  • The name and type of a variable appear in a declaration statement ending with a semicolon. A declaration for a variable that results in memory being allocated is also a definition of the variable.

     

  • Variables may be given initial values when they’re declared, and it’s good programming practice to do so.

     

  • You can protect the value of a “variable” of a basic type by using the modifier const . The compiler will check for any attempts within the program source file to modify a variable declared as const .

     

  • An lvalue is an object or expression that can appear on the left side of an assignment. Non- const variables are examples of lvalues.

     

Although I discussed quite a few basic types in this chapter, don’t be misled into thinking that’s all there are. There are some other basic types, as well as more complex types based on the basic set, as you’ll see, and eventually you’ll be creating original types of your own.

{mospagebreak title=Exercises}

The following exercises enable you to try out what you’ve learned in this chapter. If you get stuck, look back over the chapter for help. If you’re still stuck, you can download the solutions from the Downloads area of the Apress website ( http://www.apress.com ), but that really should be a last resort.

http://www.apress.com ), but that really should be a last resort. The following exercises enable you to try out what you’ve learned in this chapter. If you get stuck, look back over the chapter for help. If you’re still stuck, you can download the solutions from the Downloads area of the Apress website (http://www.apress.com), but that really should be a last resort.

Exercise 2-1. Write a program that will compute the area of a circle. The program should prompt for the radius of the circle to be entered from the keyboard, calculate the area using the formula area = pi * radius * radius , and then display the result.

Exercise 2-2. Using your solution for Exercise 2-1, improve the code so that the user can control the precision of the output by entering the number of digits required. (Hint: Use the setprecision() manipulator.)

Exercise 2-3. Create a program that converts inches to feet-and-inches— for example, an input of 77 inches should produce an output of 6 feet and 5 inches. Prompt the user to enter an integer value corresponding to the number of inches, and then make the conversion and output the result. (Hint: Use a const to store the inches-to-feet conversion rate; the modulus operator will be very helpful.)

Exercise 2-4. For your birthday you’ve been given a long tape measure and an instrument that allows you to determine angles—the angle between the horizontal and a line to the top of a tree, for instance. If you know the distance, d, you are from a tree, and the height, h, of your eye when peering into your angle-measuring device, you can calculate the height of the tree with this formula:

h+d*tan(angle)

Create a program to read h in inches, d in feet and inches, and angle in degrees from the keyboard, and output the height of the tree in feet.

NOTE There is no need to chop down any trees to verify the accuracy of your program. Just check the solutions on the Apress website ( http://www.apress.com/book/download.html ).

Exercise 2-5. Here’s an exercise for puzzle fans. Write a program that will prompt the user to enter two different positive integers. Identify in the output the value of the larger integer and the value of the smaller integer. (This can be done with what you’ve learned in this chapter!)

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye