Java. The goal of this chapter is to gain a firm understanding of the fundamental

56  Download (0)

Full text

(1)

77

C h a p t e r 3

Fundamental Data Types

 To recognize the limitations of the int and double types and the overflow and roundoff errors that can result

 To write arithmetic expressions in Java

 To use the String type to define and manipulate character strings

 To learn about the char data type

 To learn how to read program input

 To understand the copy behavior of primitive types and object references C H A P T E R G O A L S

To understand integer and floating-point numbers

T his chapter teaches how to manipulate numbers and character strings in

Java. The goal of this chapter is to gain a firm understanding of the fundamental

Java data types.

(2)

78 CHAPTER 3 Fundamental Data Types

78

In this chapter, we will use a Purse class to demonstrate several important concepts. We won’t yet reveal the implementation of the purse, but here is the public interface:

public class Purse {

/**

Constructs an empty purse.

*/

public Purse() {

// implementation }

/**

Add nickels to the purse.

@param count the number of nickels to add */

public void addNickels(int count) {

// implementation }

/**

Add dimes to the purse.

@param count the number of dimes to add

3.1 Number Types

3.1 Number Types 78

Quality Tip 3.1: Choose Descriptive Variable Names 80

Advanced Topic 3.1: Numeric Ranges and Precisions 81

Advanced Topic 3.2: Other Number Types 82 Random Fact 3.1: The Pentium Floating-Point

Bug 83

3.2 Assignment 84

Advanced Topic 3.3: Combining Assignment and Arithmetic 86

Productivity Hint 3.1: Avoid Unstable Layout 87 3.3 Constants 88

Syntax 3.1: Constant Definition 90

Quality Tip 3.2: Do Not Use Magic Numbers 92 3.4 Arithmetic and Mathematical Functions 92 Common Error 3.1: Integer Division 94

Common Error 3.2: Unbalanced Parentheses 96

Productivity Hint 3.2: On-Line Help 96

Quality Tip 3.3: White Space 97

Quality Tip 3.4: Factor Out Common Code 98 3.5 Calling Static Methods 98

Syntax 3.2: Static Method Call 99 3.6 Type Conversion 100 Syntax 3.3: Cast 101

HOWTO 3.1: Carrying Out Computations 101 Common Error 3.3: Roundoff Errors 104 Advanced Topic 3.4: Binary Numbers 105 3.7 Strings 107

Advanced Topic 3.5: Formatting Numbers 109 3.8 Reading Input 110

Productivity Hint 3.3: Reading Exception Reports 112

Advanced Topic 3.6: Reading Console Input 113

3.9 Characters 116

Random Fact 3.2: International Alphabets 116 3.10 Comparing Primitive Types and

Objects 119 C H A P T E R C O N T E N T S

(3)

3.1 Number Types 79 */

public void addDimes(int count) {

// implementation }

/**

Add quarters to the purse.

@param count the number of quarters to add */

public void addQuarters(int count) {

// implementation }

/**

Get the total value of the coins in the purse.

@return the sum of all coin values */

public double getTotal() {

// implementation }

// private instance variables }

Read through the public interface and ask yourself whether you can figure out how to use Purse objects. You should find this straightforward. There is a constructor to make a new, empty purse:

Purse myPurse = new Purse();

You can add nickels, dimes, and quarters. (For simplicity, we don’t bother with pennies, half dollars, or dollar coins.)

myPurse.addNickels(3);

myPurse.addDimes(1);

myPurse.addQuarters(2);

Now you can ask the purse object about the total value of the coins in the purse:

double totalValue = myPurse.getTotal(); // returns 0.75

If you look closely at the methods to add coins, you will see an unfamiliar data type. The count parameter has type int, which denotes an integer type. An integer is a number without a fractional part. For example, 3 is an integer, but 0.05 is not. The number zero and negative numbers are integers. Thus, the int type is more restric- tive than the double type that you saw in Chapter 2.

Why have both integer and floating-point number types? Your calculator doesn’t have a separate integer type. It uses floating-point numbers for all calculations. Why don’t we just use the double type for the coin counts?

The int type denotes integers: numbers without fractional parts.

(4)

80 CHAPTER 3 Fundamental Data Types

80

There are two reasons for having a separate integer type: one philosophical and one prag- matic. In terms of philosophy, when we think about real purses and modern American coins, we recognize that there can be only a whole number of nickels, say, in a purse. If we were to saw a nickel in half, the halves would be worthless, and dropping one of them into a purse would not increase the amount of money in the purse. By specifying that the number of nickels is an integer, we make that observation into an explicit assumption in our model. The program would have worked just as well with floating-point numbers to count the coins, but it is generally a good idea to choose programming solutions that document one’s intentions.

Pragmatically speaking, integers are more efficient than floating-point numbers. They take less storage space, are processed faster on some platforms, and don’t cause rounding errors.

Now let’s start implementing the Purse class. Any Purse object can be described by the number of nickels, dimes, and quarters that the purse currently contains. Thus, we use three instance variables to represent the state of a Purse object:

public class Purse {

. . .

private int nickels;

private int dimes;

private int quarters;

}

Now we can implement the getTotal method simply:

public double getTotal() {

return nickels * 0.05 + dimes * 0.1 + quarters * 0.25;

}

In Java, multiplication is denoted by an asterisk *, not a raised dot · or a cross , because there are no keys for these symbols on most keyboards. For example, d · 10 is written as d * 10. Do not write commas or spaces in numbers in Java. For example, 10,150.75 must be entered as 10150.75. To write numbers in exponential notation in Java, use E n instead of “ 10n”. For example, 5.0 10–3 is written as 5.0E-3.

The getTotal method computes the value of the expression nickels * 0.05 + dimes * 0.1 + quarters * 0.25

That value is a floating-point number, because multiplying an integer (such as nickels) by a floating-point number (such as 0.05) yields a floating-point number. The return statement returns the computed value as the method result, and the method exits.

Choose Descriptive Variable Names

In algebra, variable names are usually just one letter long, such as p or A, maybe with a subscript such as p1. You might be tempted to save yourself a lot of typing by using shorter variable names in your Java programs as well:



 

Quality Tip 3.1







(5)

public class Purse {

. . .

private int n;

private int d;

private int q;

}

Compare this with the previous one, though. Which one is easier to read? There is no comparison. Just reading nickels is a lot less trouble than reading n and then figuring out that it must mean “nickels”.

In practical programming, descriptive variable names are particularly important when programs are written by more than one person. It may be obvious to you that n must stand for nickels, but is it obvious to the person who needs to update your code years later, long after you were promoted (or laid off )? For that matter, will you remember yourself what n means when you look at the code six months from now?

Of course, you could use comments:

public class Purse {

. . .

private int n; // nickels private int d; // dimes private int q; // quarters }

That makes the definitions pretty clear. But in the getTotal method, you’d still have a rather cryptic computation n * 0.05 + d * 0.1 + q * 0.25. Descriptive variable names are a better choice, because they make your code easy to read without requiring comments.

Numeric Ranges and Precisions

Unfortunately, int and double values do suffer from one problem: They cannot represent arbi- trarily large integer or floating-point numbers. Integers have a range of 2,147,483,648 (about 2 billion) to 2,147,483,647 (about 2 billion). See Advanced Topic 3.4 for an explanation of these values. If you need to refer to these boundaries in your program, use the constants Integer.

MIN_VALUE and Integer.MAX_VALUE, which are defined in a class called Integer. If you want to represent the world population, you can’t use an int. Double-precision floating-point numbers are somewhat less limited; they can go up to more than 10300. However, double floating-point numbers suffer from a different problem: precision. They store only about 15 sig- nificant digits. Suppose your customers might find the price of three hundred trillion dollars ($300,000,000,000,000) for your product a bit excessive, so you want to reduce it by five cents to a more reasonable-looking $299,999,999,999,999.95. Try running the following program:

class AdvancedTopic3_1 {



















Advanced Topic 3.1



  











(6)

public static void main(String[] args) {

double originalPrice = 3E14;

double discountedPrice = originalPrice - 0.05;

double discount = originalPrice - discountedPrice;

// should be 0.05;

System.out.println(discount);

// prints 0.0625;

} }

The program prints out 0.0625, not 0.05. It is off by more than a penny!

For most of the programming projects in this book, the limited range and precision of int and double are acceptable. Just bear in mind that overflows or loss of precision can occur.

Other Number Types

If int and double are not sufficient for your computational needs, there are other data types to which you can turn. When the range of integers is not sufficient, the simplest remedy is to use the long type. Long integers have a range from

9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

To specify a long integer constant, you need to append the letter L after the number value. For example,

long price = 300000000000000L;

There is also an integer type short with shorter-than-normal integers, having a range of 32,768 to 32,767. Finally, there is a type byte with a range of 128 to 127.

The double type can represent about 15 decimal digits. There is a second floating- point type, called float, whose values take half the storage space. Computations involv- ing float execute a bit faster than those involving double, but the precision of float values—about 7 decimal digits—is insufficient for many programs. However, some graphics routines require you to use float values.

By the way, the name “floating-point” comes from the fact that the numbers are represented in the computer as a sequence of the significant digits and an indication of the position of the decimal point. For example, the numbers 250, 2.5, 0.25, and 0.025 all have the same decimal digits: 25. When a floating-point number is multiplied or divided by 10, only the position of the decimal point changes; it “floats”. This repre- sentation corresponds to numbers written in “exponential” or “scientific” notation, such as 2.5 102. (Actually, internally the numbers are represented in base 2, as binary numbers, but the principle is the same. See Advanced Topic 3.4 for more information on binary numbers.) Sometimes float values are called “single-precision”, and of course double values are “double-precision” floating-point numbers.











Advanced Topic 3.2



 

 





















(7)

If you want to compute with really large numbers, you can use big number objects.

Big number objects are objects of the BigInteger and BigDecimal classes in the java.math package. Unlike the number types such as int or double, big number objects have essentially no limits on their size and precision. However, computations with big number objects are much slower than those that involve number types. Per- haps more importantly, you can’t use the familiar arithmetic operators (+ - * /) with them. Instead, you have to use methods called add, subtract, multiply, and divide. Here is an example of how to create two big numbers and how to multiply them.

BigInteger a = new BigInteger("123456789");

BigInteger b = new BigInteger("987654321");

BigInteger c = a.multiply(b);

System.out.println(c); // prints 121932631112635269

The Pentium Floating-Point Bug

In 1994, Intel Corporation released what was then its most powerful processor, the first of the Pentium series. Unlike previous generations of Intel’s processors, the Pentium had a very fast floating-point unit. Intel’s goal was to compete aggressively with the makers of higher- end processors for engineering workstations. The Pentium was an immediate huge success.

In the summer of 1994, Dr. Thomas Nicely of Lynchburg College in Virginia ran an extensive set of computations to analyze the sums of reciprocals of certain sequences of prime numbers. The results were not always what his theory predicted, even after he took into account the inevitable roundoff errors. Then Dr. Nicely noted that the same program did produce the correct results when run on the slower 486 processor, which preceded the Pentium in Intel’s lineup. This should not have happened. The optimal roundoff behavior of floating-point calculations has been standardized by the Institute of Electrical and Electronics Engineers (IEEE), and Intel claimed to adhere to the IEEE standard in both the 486 and the Pentium processors. Upon further checking, Dr. Nicely discovered that indeed there was a very small set of numbers for which the product of two numbers was computed differently on the two processors. For example,

4,195,835 ((4,195,835 / 3,145,727) 3,145,727)

is mathematically equal to 0, and it did compute as 0 on a 486 processor. On a Pentium processor, however, the result was 256.

As it turned out, Intel had independently discovered the bug in its testing and had started to produce chips that fixed it. (Subsequent versions of the Pentium, such as the Pentium III and IV, are free of the problem.) The bug was caused by an error in a table that was used to speed up the floating-point multiplication algorithm of the processor.

Intel determined that the problem was exceedingly rare. They claimed that under normal use a typical consumer would only notice the problem once every 27,000 years. Unfortu- nately for Intel, Dr. Nicely had not been a normal user.











Random Fact 3.1





















 

(8)

Now Intel had a real problem on its hands. It figured that replacing all the Pen- tium processors that it had already sold would cost it a great deal of money. Intel already had more orders for the chip than it could produce, and it would be partic- ularly galling to have to give out the scarce chips as free replacements instead of selling them. Intel’s management decided to punt on the issue and initially offered to replace the processors only for those customers who could prove that their work required absolute precision in mathematical calculations. Naturally, that did not go over well with the hundreds of thousands of customers who had paid retail prices of $700 and more for a Pentium chip and did not want to live with the nagging feeling that perhaps, one day, their income tax program would produce a faulty return.

Ultimately, Intel had to cave in to public demand and replaced all defective chips, at a cost of about 475 million dollars.

What do you think? Intel claims that the probability of the bug occurring in any cal- culation is extremely small—smaller than many chances you take every day, such as driv- ing to work in an automobile. Indeed, many users had used their Pentium computers for many months without reporting any ill effects, and the computations that Professor Nicely was doing are hardly examples of typical user needs. As a result of its public rela- tions blunder, Intel ended up paying a large amount of money. Undoubtedly, some of that money was added to chip prices and thus actually paid by Intel’s customers. Also, a large number of processors, whose manufacture consumed energy and caused some envi- ronmental impact, were destroyed without benefiting anyone. Could Intel have been jus- tified in wanting to replace only the processors of those users who could reasonably be expected to suffer an impact from the problem?

Suppose that, instead of stonewalling, Intel had offered you the choice of a free replacement processor or a $200 rebate. What would you have done? Would you have replaced your faulty chip, or would you have taken your chance and pocketed the money?

Here is the constructor of the Purse class:

public Purse() {

nickels = 0;

dimes = 0;

quarters = 0;

}

The = operator is called the assignment operator. On the left, you need a variable name.

The right-hand side can be a single value or an expression. The assignment operator sets the variable to the given value. So far, that’s straightforward. But now let’s look at a more interesting use of the assignment operator, in the addNickels method.





















3.2 Assignment

(9)

public void addNickels(int count) {

nickels = nickels + count;

}

It means, “Compute the value of the expression nickels + count, and place the result again into the variable nickels.” (See Figure 1.)

The = sign doesn’t mean that the left-hand side is equal to the right-hand side but that the right-hand-side value is copied into the left-hand-side variable. You should not confuse this assignment operation with the = used in algebra to denote equality. The assignment operator is an instruction to do something, namely place a value into a variable. The mathematical equal- ity states the fact that two values are equal. For example, in Java it is perfectly legal to write

nickels = nickels + 1;

It means to look up the value stored in the variable nickels, to add 1 to it, and to stuff the sum back into nickels. (See Figure 2.) The net effect of executing this

F i g u r e 1 Assignment

F i g u r e 2

Incrementing a Variable count =

nickels =

nickels + count

nickels =

nickels + 1

(10)

statement is to increment nickels by 1. Of course, in mathematics it would make no sense to write that n = n + 1; no integer can equal itself plus 1.

The concepts of assignment and equality have no relationship with each other, and it is a bit unfortunate that the Java language (following C and C++) uses = to denote assignment. Other pro- gramming languages use a symbol such as <- or :=, which avoids the confusion.

Consider once more the statement nickels = nickels + 1. This statement increments the nickels variable. For example, if nickels was 3 before execution of the statement, it is set to 4 after- wards. This increment operation is so common when writing pro- grams that there is a special shorthand for it, namely

nickels++;

This statement has exactly the same effect—namely, to add 1 to nickels—but it is easier to type. As you might have guessed, there is also a decrement operator --. The statement

nickels--;

subtracts 1 from nickels.

Combining Assignment and Arithmetic

In Java you can combine arithmetic and assignment. For example, the instruction nickels += count;

is a shortcut for

nickels = nickels + count;

Similarly,

nickels *= 2;

is another way of writing nickels = nickels * 2;

Many programmers find this a convenient shortcut. If you like it, go ahead and use it in your own code. For simplicity, we won’t use it in this book, though.

Advanced Topic 3.3













Assignment to a variable is not the same as mathematical equality.

The ++ and -- operators increment and decrement a variable.

(11)

Avoid Unstable Layout

You should arrange program code and comments so that the program is easy to read. For example, you should not cram all statements on a single line, and you should make sure that braces { } line up.

However, you should be careful when you embark on beautification efforts. Some programmers like to line up the = signs in a series of assignments, like this:

nickels = 0;

dimes = 0;

quarters = 0;

This looks very neat, but the layout is not stable. Suppose you add a line like the one at the bottom of this:

nickels = 0;

dimes = 0;

quarters = 0;

halfDollars = 0;

Oops, now the = signs no longer line up, and you have the extra work of lining them up again.

Here is another example. Suppose you have a comment that goes over multiple lines:

// In this test class, we compute the value of a set of coins.

// We add a number of nickels, dimes, and quarters // to a purse. We then get and display the total value.

When the program is extended to work for half-dollar coins as well, you must modify the comment to reflect that change.

// In this test class, we compute the value of a set of coins.

// We add a number of nickels, dimes, quarters, and half-dollars // to a purse. We then get and display the total value.

Now you need to rearrange the // to fix up the comment. This scheme is a disincentive to keep comments up to date. Don’t do it. Instead, for comments that are longer than one line, use the /*...*/ style for comments, and block off the entire comment like this:

/*

In this test class, we compute the value of a set of coins.

We add a number of nickels, dimes, and quarters to a purse. We then get and display the total value.

*/

You may not care about these issues. Perhaps you plan to beautify your program just before it is finished, when you are about to turn in your homework. That is not a

Productivity Hint 3.1































(12)

particularly useful approach. In practice, programs are never finished. They are con- tinuously improved and updated. It is better to develop the habit of laying out your programs well from the start and keeping them legible at all times. As a conse- quence, you should avoid layout schemes that are hard to maintain.

Consider once again the getTotal method, paying attention to whether it is easy to understand the code.

public double getTotal() {

return nickels * 0.05 + dimes * 0.1 + quarters * 0.25;

}

Most of the code is self-documenting. However, the three numeric quantities, 0.05, 0.1, and 0.25, are included in the arithmetic expression without any explanation. Of course, in this case, you know that the value of a nickel is five cents, which explains the 0.05, and so on. However, the next person who needs to maintain this code may live in another country and may not know that a nickel is worth five cents.

Thus, it is a good idea to use symbolic names for all values, even those that appear obvious. Here is a clearer version of the computation of the total:

double nickelValue = 0.05;

double dimeValue = 0.1;

double quarterValue = 0.25;

return nickels * nickelValue + dimes * dimeValue + quarters * quarterValue;

There is another improvement we can make. There is a difference between the nickels and nickelValue variables. The nickels variable can truly vary over the life of the program, as more coins are added to the purse. But nickelValue is always 0.05. It is a constant.

In Java, constants are identified with the keyword final. A variable tagged as final can never change after it has been set. If you try to change the value of a final variable, the compiler will report an error and your program will not compile.

Many programmers use all-uppercase names for constants (final variables), such as NICKEL_VALUE. That way, it is easy to distinguish between variables (with mostly lowercase letters) and constants. We will follow this convention in this book. However, this rule is a matter of good style, not a requirement of the Java language. The compiler will not complain if you give a final vari- able a name with lowercase letters.





3.3 Constants

A final variable is a constant. Once its value has been set, it cannot be changed.

Use named constants to make your programs easier to read and maintain.

(13)

Here is the improved version of the getTotal method:

public double getTotal() {

final double NICKEL_VALUE = 0.05;

final double DIME_VALUE = 0.1;

final double QUARTER_VALUE = 0.25;

return nickels * NICKEL_VALUE + dimes * DIME_VALUE + quarters * QUARTER_VALUE;

}

In this example, the constants are needed only inside one method of the class. Fre- quently, a constant value is needed in several methods. Then you need to declare it together with the instance variables of the class and tag it as static final. The mean- ing of the keyword static will be explained in Chapter 6.

public class Purse {

// methods . . . // constants

private static final double NICKEL_VALUE = 0.05;

private static final double DIME_VALUE = 0.1;

private static final double QUARTER_VALUE = 0.25;

// instance variables private int nickels;

private int dimes;

private int quarters;

}

Here we defined the constants to be private because we didn’t think they were of interest to users of the Purse class. However, it is also possible to declare constants as public:

public static final double NICKEL_VALUE = 0.05;

Then methods of other classes can access the constant as Purse.NICKEL_VALUE. The Math class from the standard library defines a couple of useful constants:

public class Math {

. . .

public static final double E = 2.7182818284590452354;

public static final double PI = 3.14159265358979323846;

}

You can refer to these constants as Math.PI and Math.E in any of your methods. For example,

double circumference = Math.PI * diameter;

(14)

File Purse.java

1 /**

2 A purse computes the total value of a collection of coins.

3 */

4 public class Purse 5 {

6 /**

7 Constructs an empty purse.

8 */

9 public Purse() 10 {

11 nickels = 0;

12 dimes = 0;

13 quarters = 0;

14 } 15

16 /**

17 Add nickels to the purse.

18 @param count the number of nickels to add 19 */

20 public void addNickels(int count) 21 {

22 nickels = nickels + count;

23 } 24 /**

25 Add dimes to the purse.

26 @param count the number of dimes to add

Syntax 3.1 : Constant Definition

In a method:

final typeName variableName expression; In a class:

accessSpecifier static final typeName variableName = expression;

Example:

final double NICKEL_VALUE = 0.05;

public static final double LITERS_PER_GALLON = 3.785;

Purpose:

To define a constant of a particular type



(15)

27 */

28 public void addDimes(int count) 29 {

30 dimes = dimes + count;

31 } 32

33 /**

34 Add quarters to the purse.

35 @param count the number of quarters to add 36 */

37 public void addQuarters(int count) 38 {

39 quarters = quarters + count;

40 } 41

42 /**

43 Get the total value of the coins in the purse.

44 @return the sum of all coin values 45 */

46 public double getTotal() 47 {

48 return nickels * NICKEL_VALUE

49 + dimes * DIME_VALUE + quarters * QUARTER_VALUE;

50 } 51

52 private static final double NICKEL_VALUE = 0.05;

53 private static final double DIME_VALUE = 0.1;

54 private static final double QUARTER_VALUE = 0.25;

55

56 private int nickels;

57 private int dimes;

58 private int quarters;

59 }

File PurseTest.java

1 /**

2 This program tests the Purse class.

3 */

4 public class PurseTest 5 {

6 public static void main(String[] args) 7 {

8 Purse myPurse = new Purse();

9

10 myPurse.addNickels(3);

11 myPurse.addDimes(1);

12 myPurse.addQuarters(2);

13 double totalValue = myPurse.getTotal();

(16)

14 System.out.print("The total is ");

15 System.out.println(totalValue);

16 } 17 }

Do Not Use Magic Numbers

A magic number is a numeric constant that appears in your code without explanation. For example, consider the following scary example that actually occurs in the Java library source:

h = 31 * h + ch;

Why 31? The number of days in January? One less than the number of bits in an integer?

Actually, this code computes a “hash code” from a string—a number that is derived from the characters in such a way that different strings are likely to yield different hash codes.

The value 31 turns out to scramble the character values nicely.

You should use a named constant instead:

final int HASH_MULTIPLIER = 31;

h = HASH_MULTIPLIER * h + ch;

You should never use magic numbers in your code. Any number that is not completely self- explanatory should be declared as a named constant. Even the most reasonable cosmic con- stant is going to change one day. You think there are 365 days in a year? Your customers on Mars are going to be pretty unhappy about your silly prejudice. Make a constant

final int DAYS_PER_YEAR = 365;

By the way, the device

final int THREE_HUNDRED_AND_SIXTY_FIVE = 365;

is counterproductive and frowned upon.

You already saw how to add, subtract, and multiply values. Division is indicated with a /, not a fraction bar. For example,

becomes

(a + b) / 2

Parentheses are used just as in algebra: to indicate in which order the subexpressions should be computed. For example, in the expression (a + b) / 2, the sum a + b is computed first, and then the sum is divided by 2. In contrast, in the expression

a + b / 2

Quality Tip 3.2



















3.4 Arithmetic and Mathematical Functions

ab ---2

(17)

only b is divided by 2, and then the sum of a and b / 2 is formed. Just as in regular alge- braic notation, multiplication and division bind more strongly than addition and subtraction. For example, in the expression a + b / 2, the / is carried out first, even though the + operation occurs further to the left.

Division works as you would expect, as long as at least one of the numbers involved is a floating-point number. That is,

7.0 / 4.0 7 / 4.0 7.0 / 4

all yield 1.75. However, if both numbers are integers, then the result of the division is always an integer, with the remainder discarded. That is,

7 / 4

evaluates to 1, because 7 divided by 4 is 1 with a remainder of 3 (which is discarded).

This can be a source of subtle programming errors—see Common Error 3.1.

If you are interested only in the remainder of an integer division, use the % operator:

7 % 4

is 3, the remainder of the integer division of 7 by 4. The % symbol has no analog in algebra. It was chosen because it looks similar to /, and the remainder operation is related to division.

Here is a typical use for the integer / and % operations. Suppose you want to know the value of the coins in a purse in dollars and cents. You can compute the value as an integer, denominated in cents, and then compute the whole dollar amount and the remaining change:

final int PENNIES_PER_NICKEL = 5;

final int PENNIES_PER_DIME = 10;

final int PENNIES_PER_QUARTER = 25;

final int PENNIES_PER_DOLLAR = 100;

// compute total value in pennies

int total = nickels * PENNIES_PER_NICKEL + dimes * PENNIES_PER_DIME

+ quarters * PENNIES_PER_QUARTER;

// use integer division to convert to dollars, cents int dollars = total / PENNIES_PER_DOLLAR;

int cents = total % PENNIES_PER_DOLLAR;

For example, if total is 243, then dollars is set to 2 and cents to 43.

To take the square root of a number, you use the Math.sqrt method. For example, is written as Math.sqrt(x). To compute xn, you write Math.pow(x, n). However, to compute x2 it is significantly more efficient simply to compute x * x.

x

If both arguments of the / operator are integers, the result is an integer and the remainder is discarded.

The % operator computes the remainder of a division.

(18)

Integer Division

It is unfortunate that Java uses the same symbol, namely /, for both integer and floating-point division. These are really quite different operations. It is a common error to use integer divi- sion by accident. Consider this program segment that computes the average of three integers.

int s1 = 5; // score of test 1 int s2 = 6; // score of test 2 int s3 = 3; // score of test 3

double average = (s1 + s2 + s3) / 3; // Error System.out.print("Your average score is ");

System.out.println(average);

What could be wrong with that? Of course, the average of s1, s2, and s3 is

Here, however, the / does not mean division in the mathematical sense. It denotes integer division, because the values s1 + s2 + s3 and 3 are both integers. For example, if the scores add up to 14, the average is computed to be 4, the result of the integer divi- sion of 14 by 3. That integer 4 is then moved into the floating-point variable average. The remedy is to make the numerator or denominator into a floating-point number:

double total = s1 + s2 + s3;

double average = total / 3;

or

double average = (s1 + s2 + s3) / 3.0;

As you can see, the visual effect of the /, Math.sqrt, and Math.pow notations is to flatten out mathematical terms. In algebra, you use fractions, superscripts for exponents, and radical signs for roots to arrange expressions in a compact two-dimensional form. In Java, you have to write all expressions in a linear arrangement. For example, the subexpression

of the quadratic formula becomes

(-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a)

Figure 3 shows how to analyze such an expression. With complicated expressions like these, it is not always easy to keep the parentheses ( ... ) matched—see Common Error 3.2.

Table 1 shows additional methods of the Math class. Inputs and outputs are floating- point numbers.

Common Error 3.1



















1 s2 s3 ---3

b b24ac 2a --- The Math class contains

methods sqrt and pow to compute square roots and powers.

(19)

F i g u r e 3

Analyzing an Expression

(–b + Math.sqrt(b * b – 4 * a * c)) / (2 * a) b2

b2– 4ac b2– 4ac

4ac 2a

2a –b + b2– 4ac

–b + b2– 4ac

Ta b l e 1

Mathematical Methods

Math.pow(x, y) x y (x0, or x=0 and y0, or x0 and y is an integer) Math.sqrt(x) Square root of x (0)

Sine of x (x in radians) Cosine of x

Tangent of x

Arc sine (sin–1x [–/2, /2], x [–1,1]) Arc cosine (cos–1x[0,], x [–1,1]) Arc tangent (tan–1x (–/2, /2))

Arc tangent (tan–1(y/x)  [–/2,/2], x may be 0 Convert x radians to degrees (i.e., returns x 180/) Convert x degrees to radians (i.e., returns x /180)

ex

Natural log (ln(x), x >0) Math.sin(x)

Math.cos(x) Math.tan(x) Math.asin(x) Math.acos(x) Math.atan(x) Math.atan2(y, x) Math.toRadians(x) Math.toDegrees(x) Math.exp(x) Math.log(x)

Closest integer to x (as a long) Math.round(x)

Smallest integer  x (as a double) Math.ceil(x)

Largest integer x (as a double) Math.floor(x)

Absolute value |x|

Math.abs(x)

Function Returns

(20)

Unbalanced Parentheses Consider the expression

1.5 * ((-(b - Math.sqrt(b * b - 4 * a * c)) / (2 * a))

What is wrong with it? Count the parentheses. There are five opening parentheses ( and four closing parentheses ). The parentheses are unbalanced. This kind of typing error is very common with complicated expressions. Now consider this expression.

1.5 * (Math.sqrt(b * b - 4 * a * c))) - ((b / (2 * a))

This expression has five opening parentheses ( and five closing parentheses ), but it is still not correct. In the middle of the expression,

1.5 * (Math.sqrt(b * b - 4 * a * c))) - ((b / (2 * a))

there are only two opening parentheses ( but three closing parentheses ), which is an error. In the middle of an expression, the count of opening parentheses ( must be greater than or equal to the count of closing parentheses ), and at the end of the expression the two counts must be the same.

Here is a simple trick to make the counting easier without using pencil and paper. It is difficult for the brain to keep two counts simultaneously, so keep only one count when scanning the expression. Start with 1 at the first opening parenthesis; add 1 whenever you see an opening parenthesis; and subtract 1 whenever you see a closing parenthesis.

Say the numbers aloud as you scan the expression. If the count ever drops below zero, or if it is not zero at the end, the parentheses are unbalanced. For example, when scanning the previous expression, you would mutter

1.5 * (Math.sqrt(b * b - 4 * a * c) ) ) - ((b / (2 * a)) 1 2 1 0 –1

and you would find the error.

On-Line Help

The Java library has hundreds of classes and thousands of methods. It is neither necessary nor useful trying to memorize them. Instead, you should become familiar with using the on-line documentation. You can download the documentation from http://java.sun.com/

j2se/1.3/docs.html. Install the documentation set and point your browser to your Java installation directory /docs/api/index.html. Alternatively, you can browse http://

java.sun.com/j2se/1.3/docs/api/index.html. For example, if you are not sure how the pow method works, or cannot remember whether it was called pow or power, the on-line help

Common Error 3.2





















Productivity Hint 3.2









(21)

can give you the answer quickly. Click on the Math class in the class window on the left, and look at the method summary in the main window (see Figure 4).

If you use javadoc to document your own classes, then this documentation format will look extremely familiar to you. The programmers who implement the Java library use javadoc themselves. They too document every class, every method, every parame- ter, and every return value, and then use javadoc to extract the documentation in HTML format.

White Space

The compiler does not care whether you write your entire program onto a single line or place every symbol onto a separate line. The human reader, though, cares very much. You should use blank lines to group your code visually into sections. For example, you can signal to the reader that an output prompt and the corresponding input statement belong together by inserting a blank line before and after the group. You will find many exam- ples in the source code listings in this book.

White space inside expressions is also important. It is easier to read x1 = (-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a);

F i g u r e 4 On-Line Help





















Quality Tip 3.3









(22)

than

X1=(-b+Math.sqrt(b*b-4*a*c))/(2*a);

Simply put spaces around all operators + - * / % =. However, don’t put a space after a unary minus: a - used to negate a single quantity, as in -b. That way, it can be easily distinguished from a binary minus, as in a - b. Don’t put spaces between a method name and the parentheses, but do put a space after every Java keyword. That makes it easy to see that the sqrt in Math.sqrt(x) is a method name, whereas the if in if (x > 0)... is a keyword.

Factor Out Common Code

Suppose you want to find both solutions of the quadratic equation ax2 + bx + c = 0. The quadratic formula tells us that the solutions are

In Java, there is no analog to the operation, which indicates how to obtain two solutions simultaneously. Both solutions must be computed separately:

x1 = (-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a);

x2 = (-b - Math.sqrt(b * b - 4 * a * c)) / (2 * a);

This approach has two problems. First, the computation of Math.sqrt(b * b - 4 * a * c) is carried out twice, which wastes time. Second, whenever the same code is replicated, the possibility of a typing error increases. The remedy is to factor out the common code:

double root = Math.sqrt(b * b - 4 * a * c);

x1 = (-b + root) / (2 * a);

x2 = (-b - root) / (2 * a);

You could go even further and factor out the computation of 2 * a, but the gain from factoring out very simple computations is too small to warrant the effort.

In the preceding section, you encountered the Math class, which contains a collection of helpful methods for carrying out mathematical computations.

There is one important difference between the methods of the Math class, such as the sqrt method, and the methods that you have seen so far (such as getTotal or







Quality Tip 3.4



x1,2 b b24ac ---2a

















3.5 Calling Static Methods

(23)

println). The getTotal and println methods, as you have seen, operate on an object such as myPurse or System.out. In contrast, the sqrt method does not operate on any object. That is, you don’t call

double x = 4;

double root = x.sqrt(); // Error

The reason is that, in Java, numbers are not objects, so you can never invoke a method on a number. Instead, you pass a number as an explicit parameter to a method, enclosing the number in parentheses after the method name. For example, the number value x can be a parameter of the Math.sqrt method: Math.sqrt(x).

This call makes it appear as if the sqrt method is applied to an object called Math, because Math precedes sqrt just as myPurse precedes getTotal in a method call myPurse.getTotal(). However, Math is a class, not an object. A method such as Math.round that does not operate on any object is called a static method. (The term “static” is a historical holdover from C and C++ that has nothing to do with the usual meaning of the word.) Static methods do not operate on objects, but they are still defined inside classes. You must specify the class to which the sqrt method belongs—hence the call is Math.sqrt(x).

How can you tell whether Math is a class or an object? All classes in the Java library start with an uppercase letter (such as System). Objects and methods start with a lowercase letter (such as out and println). You can tell objects and methods apart because method calls are followed by parentheses. Therefore, System.out.println() denotes a call of the println method on the out object inside the System class. On the other hand, Math.sqrt(x) denotes a call to the sqrt method inside the Math class. This use of upper- and lowercase letters is merely a convention, not a rule of the Java language. It is, however, a convention that the authors of the Java class libraries follow consistently. You should do the same in your programs. If you give names to objects or methods that start with an uppercase let- ter, you will likely confuse your fellow programmers. Therefore, we strongly recom- mend that you follow the standard naming convention.

Syntax 3.2 : Static Method Call

ClassName.methodName(parameters)

Example:

Math.sqrt(4) Purpose:

To invoke a static method (a method that does not operate on an object) and supply its parameters

A static method does not operate on an object.

(24)

When you make an assignment of an expression into a variable, the types of the variable and the expression must be compatible. For example, it is an error to assign

double total = "a lot"; // Error

because total is a floating-point variable and "a lot" is a string. It is, however, legal to store an integer expression in a double variable:

int dollars = 2;

double total = dollars; // OK

In Java, you cannot assign a floating-point expression to an integer variable.

double total = . . .;

int dollars = total; // Error

You must convert the floating-point value to integer with a cast:

int dollars = (int)total;

The cast (int) converts the floating-point value total to an integer.

The effect of the cast is to discard the fractional part. For example, if total is 13.75, then dollars is set to 13. If you want to convert the value of a floating-point expression to an integer, you need to enclose the expression in parentheses to ensure that it is computed first:

int pennies = (int)(total * 100);

This is different from the expression int pennies = (int)total * 100;

In the second expression, total is first converted to an integer, and then the resulting integer is multiplied by 100. For example, if total is 13.75, then the first expression computes total * 100, or 1375, and then converts that value to the integer 1375. In the second expression, total is first cast to the integer 13, and then the integer is multi- plied by 100, yielding 1300. Normally, you will want to apply the integer conversion after all other computations, so that your computations use the full precision of their input values. That means you should enclose your computation in parentheses and apply the cast to the expression in parentheses.

There is a good reason why you must use a cast in Java when you convert a floating- point number to an integer: The conversion loses information. You must confirm that you agree to that information loss. Java is quite strict about this. You must use a cast when- ever there is the possibility of information loss. A cast always has the form (typeName), for example (int) or (float).

Actually, simply using an (int) cast to convert a floating-point number to an integer is not always a good idea. Consider the following example:

double price = 44.95;

int dollars = (int)price; // sets dollars to 44

3.6 Type Conversion

You use a cast (typeName) to convert a value to a different type.

(25)

What did you want to achieve? Did you want to get the number of dollars in the price?

Then dropping the fractional part is the right thing to do. Or did you want to get the approximate dollar amount? Then you really want to round up when the fractional part is 0.5 or larger.

One way to round to the nearest integer is to add 0.5, then cast to an integer:

double price = 44.95;

int dollars = (int)(price + 0.5); // OK for positive values System.out.print("The price is approximately $")

System.out.println(dollars); // prints 45

Adding 0.5 and casting to the int type works, because it turns all values that are between 44.50 and 45.4999... into 45.

Actually, there is a better way. Simply adding 0.5 works fine for positive numbers, but it doesn’t work correctly for negative numbers.

Instead, use the Math.round method in the standard Java library. It works for both positive and negative numbers. However, that method returns a long integer, because large floating-point numbers cannot be stored in an int. You need to cast the return value to an int:

int dollars = (int)Math.round(price); // better

Carrying Out Computations

Many programming problems require that you use mathematical formulas to compute values. It is not always obvious how to turn a problem statement into a sequence of mathematical formulas and, ultimately, statements in the Java programming language.

Step 1

Understand the problem: What are the inputs? What are the desired outputs?

For example, suppose you are asked to simulate a postage stamp vending machine. A customer inserts money into the vending machine. Then the customer pushes a “First class stamps”

Syntax 3.3 : Cast

(typeName) expression Example:

(int)(x + 0.5)

(int)Math.round(100 * f) Purpose:

To convert an expression to a different type

HOWTO 3.1









Use the Math.round method to round a floating-point number to the nearest integer.

(26)

button. The vending machine gives out as many first-class stamps as the customer paid for. (A first-class stamp cost 34 cents at the time this book was written.) Finally, the customer pushes a “Penny stamps” button. The machine gives the change in penny (1-cent) stamps.

In this problem, there is one input:

 The amount of money the customer inserts There are two desired outputs:

 The number of first-class stamps that the machine returns

 The number of penny stamps that the machine returns

Step 2

Work out examples by hand

This is a very important step. If you can’t compute a couple of solutions by hand, it’s unlikely that you’ll be able to write a program that automates the computation.

Let’s assume that a first-class stamp costs 34 cents and the customer inserts $1.00.

That’s enough for two stamps (68 cents) but not enough for three stamps ($1.02).

Therefore, the machine returns 2 first-class stamps and 32 penny stamps.

Step 3

Find mathematical equations that compute the answers

Given an amount of money and the price of a first-class stamp, how can you compute how many first-class stamps can be purchased with the money? Clearly, the answer is related to the quotient

For example, suppose the customer paid $1.00. Use a pocket calculator to compute the quotient: $1.00/$0.34 ≈ 2.9412.

How do you get “2 stamps” out of 2.9412? It’s the integer part. By discarding the fractional part, you get the number of whole stamps that the customer has purchased.

In mathematical notation,

where x denotes the largest integer ≤ x. That function is sometimes called the “floor function”.

You now know how to compute the number of stamps that are given out when the customer pushes the “First-class stamps” button. When the customer gets the stamps, the amount of money is reduced by the value of the stamps purchased. For example, if the customer gets two stamps, the remaining money is $0.32. It is the difference between

$1.00 and 2 $0.34. Here is the general formula:

remaining money money − number of first-class stamps price of first-class stamp How many penny stamps does the remaining money buy? That’s easy. If $0.32 is left, the customer gets 32 stamps. In general, the number of penny stamps is

number of penny stamps 100 remaining money































amount of money price of first-class stamp ---

number of first-class stamps money

price of first-class stamp ---







(27)

Step 4

Turn the mathematical equations into Java statements

In Java, you can compute the integer part of a nonnegative floating-point value by apply- ing an (int) cast. Therefore, you can compute the number of first-class stamps with the following statement:

firstClassStamps =

(int)(money / FIRST_CLASS_STAMP_PRICE);

money = money -

firstClassStamps * FIRST_CLASS_STAMP_PRICE;

Finally, the number of penny stamps is pennyStamps = 100 * money;

That’s not quite right, though. The value of pennyStamps should be an integer, but the right hand side is a floating-point number. Therefore, the correct statement is

pennyStamps = (int)Math.round(100 * money);

Step 5

Build a class that carries out your computations

HOWTO 2.1 explains how to develop a class by finding methods and instance variables.

In our case, we can find three methods:

 void insert(double amount)

 int giveFirstClassStamps()

 int givePennyStamps()

The state of a vending machine can be described by the amount of money that the cus- tomer has available for purchases. Therefore, we supply one instance variable, money.

Here is the implementation:

public class StampMachine {

public StampMachine() {

money = 0;

}

public void insert(double amount) {

money = money + amount;

}

public int giveFirstClassStamps() {

int firstClassStamps =

(int)(money / FIRST_CLASS_STAMP_PRICE);

money = money -

firstClassStamps * FIRST_CLASS_STAMP_PRICE;

































Figure

Updating...

References

Related subjects :