Working with Numbers and Expressions

In Book II, Chapter 2, you discover the various primitive numeric types that are supported by Java. In this chapter, you build on that knowledge by doing basic operations with numbers. Much of this chapter focuses on the complex topic of expressions, which combine numbers with operators to perform calculations. But this chapter also covers techniques for formatting numbers when you display them and performing advanced calculations using the Math class. In addition, you find out why Java's math operations sometimes produce results you might not expect.

Working with Arithmetic Operators

An operator is a special symbol or keyword that's used to designate a mathematical operation or some other type of operation that can be performed on one or more values, called operands. In all, Java has about 40 different operators. This chapter focuses on the operators that do arithmetic. These arithmetic operators perform basic arithmetic operations, such as addition, subtraction, multiplication, and division. In all, there are 7 of them. Table 3-1 summarizes them.

Table 3-1: Java's Arithmetic Operators

Open table as spreadsheet

Operator

Description

+

Addition

-

Subtraction

*

Multiplication

/

Division

%

Remainder

++

Increment

Decrement

The following section of code can help clarify how these operators work for int types:

int a = 21, b = 6; int c = a + b; // c is 27 int d = a - b; // d is 15 int e = a * b; // e is 126 int f = a / b; // f is 3 (21 / 6 is 3 remainder 3) int g = a % b; // g is 3 (20 / 6 is 3 remainder 3) a++; // a is now 22 b--; // b is now 5

Notice that for division, the result is truncated. Thus 21/6 returns 3, not 3.5. For more information about integer division, see the section "Dividing Integers" later in this chapter.

Here's how the operators work for double values:

double x = 5.5, y = 2.0; double m = x + y; // m is 7.5 double n = x - y; // n is 3.5 double o = x * y; // o is 11.0 double p = x / y; // p is 2.75 double q = x % y; // q is 1.5 x++; // x is now 6.5 y--; // y is now 1.0

  Warning 

When you divide two int values, the result is an integer value, even if you assign it to a double variable. For example:

int a = 21, b = 6; double answer = a / b; // answer = 3.0

Categorizing operators by the number of operands

A common way to categorize Java's operators is by the number of operands the operator works on. Categorizing the operators in this way, there are three types:

If that's not what you want, you can cast one of the operands to a double before performing the division, like this:

int a = 21, b = 6; double answer = (double)a / b; // answer = 3.5

The moral of the story is that if you want to divide int values and get an accurate double result, you must cast at least one of the int values to a double.

  Tip 

Here are a few additional things to think about tonight as you lay awake pondering the wonder of Java's arithmetic operators:

  • In algebra, you can write a number right next to a variable to imply multiplication. For example, 4x means "four times x." Not so in Java. The following statement doesn't compile:

    int x; y = 4x; // error, won't compile

  • The remainder operator (%) is also called a modulus operator. It returns the remainder when the first operand is divided by the second operand.

    The remainder operator is often used to determine if one number is evenly divisible by another, in which case the result is 0. For more information, see the next section, "Dividing Integers."

  • All operators, including the arithmetic variety, are treated as separators in Java. As a result, any use of white space in an expression is optional. Thus the following two statements are equivalent:

    a = ( (x + 4) * 7 ) / (y * x); a=((x+4)*7)/(y*x);

    Just remember that a little bit of white space never hurt anyone, and sometimes it helps make Java a little more readable.

Dividing Integers

When you divide one integer into another, the result is always another integer. Any remainder is simply discarded, and the answer is not rounded up. For example, 5/4 gives the result 1, and 3/4 gives the result 0. If you want to know that 5/4 is actually 1.25 or that 3/4 is actually 0.75, you need to use floats or doubles instead of integers.

If you need to know what the remainder is when you divide two integers, use the remainder operator (%). For example, suppose you have a certain number of marbles to give away and a certain number of children to give them to. The program in Listing 3-1 lets you enter the number of marbles and the number of children. Then it calculates the number of marbles to give to each child, and the number of marbles you have left over.

Here's a sample of the console output for this program, where the number of marbles entered is 93 and the number of children is 5:

Welcome to the marble divvy upper. Number of marbles: 93 Number of children: 5 Give each child 18 marbles. You will have 3 marbles left over.

Listing 3-1: A Program That Divvies Up Marbles

import java.util.Scanner; → 1 public class MarblesApp { static Scanner sc = new Scanner(System.in); → 5 public static void main(String[] args) { // declarations → 9 int numberOfMarbles; int numberOfChildren; int marblesPerChild; int marblesLeftOver; // get the input data → 15 System.out.println("Welcome to the marble divvy upper."); System.out.print("Number of marbles: "); numberOfMarbles = sc.nextInt(); System.out.print("Number of children: "); numberOfChildren = sc.nextInt(); // calculate the results marblesPerChild = numberOfMarbles / numberOfChildren; → 23 marblesLeftOver = numberOfMarbles % numberOfChildren; → 24 // print the results → 26 System.out.println("Give each child " + marblesPerChild + " marbles."); System.out.println("You will have " + marblesLeftOver + " marbles left over."); } }

The following paragraphs describe the key lines in this program:

1

Imports the java.util.Scanner class so the program can use it to get input from the user.

5

Creates the Scanner object and assigns it to a class variable so it can be used in any method in the class.

9

The next four lines declare the local variables used by the program.

15

The next five lines get the input from the user.

23

Calculates the number of marbles to give to each child by using integer division, which discards the remainder.

24

Calculates the number of marbles left over.

26

The next two statements print the results.

Open table as spreadsheet

  Tip 

It's probably obvious if you think about it, but you should realize that if you use integer division to divide a by b, then the result times b plus the remainder equals a. In other words:

int a = 29; // any value will do int b = 3; // any value will do int c = a / b; int d = a % b; int e = (c * b) + d; // e will always equal a

Combining Operators

You can combine operators to form complicated expressions. When you do, the order in which the operations are carried out is determined by the precedence of each operator in the expression. The order of precedence for the arithmetic operators is:

For example, in the expression a + b * c, multiplication has a higher precedence than addition. Thus b is multiplied by c first. Then the result of that multiplication is added to a.

If an expression includes two or more operators at the same order of precedence, the operators are evaluated left to right. Thus, in the expression a * b/c, a is first multiplied by b, and then the result is divided by c.

If you want, you can use parentheses to change the order in which operations are performed. Operations within parentheses are always performed before operations that aren't in parentheses. Thus, in the expression (a + b) * c, a is added to b first. Then the result is multiplied by c.

If an expression has two or more sets of parentheses, the operations in the innermost set are performed first. For example, in the expression (a * (b + c))/d, b is first added to c. Then the result is multiplied by a. And finally, that result is divided by d.

  Tip 

Apart from the increment and decrement operators, these precedence rules and the use of parentheses are the same as they are for basic algebra. So if you were paying attention in the eighth grade, precedence should make sense.

  Warning 

With double or float values, changing the left to right order for operators with the same precedence doesn't affect the result. However, with integer types, it can make a huge difference if division is involved. For example, consider these statements:

int a = 5, b = 6, c = 7; int d1 = a * b / c; // d1 is 4 int d2 = a * (b / c); // d2 is 0

This difference occurs because integer division always returns an integer result, which is a truncated version of the actual result. Thus, in the first expression, a is first multiplied by b, giving a result of 30. Then, this result is divided by c. Truncating the answer gives a result of 4. But in the second expression, b is first divided by c, which gives a truncated result of 0. Then, this result is multiplied by a, giving a final answer of 0.

Using the Unary Plus and Minus Operators

The plus and minus unary operators let you change the sign of an operand. Note that the actual operator used for these operations is the same as the binary addition and subtraction operators. The compiler figures out whether you mean to use the binary or the unary version of these operators by examining the expression.

  Tip 

The unary minus operator doesn't necessarily make an operand have a negative value. Instead, it changes whatever sign the operand has to start with. Thus, if the operand starts with a positive value, the unary minus operator changes it to negative. But if the operand starts with a negative value, the unary minus operator makes it positive. The following examples illustrate this point:

int a = 5; // a is 5 int b = -a; // b is -5 int c = -b; // c is +5

  TECHNICAL STAUFF 

Interestingly enough, the unary plus operator doesn't actually do anything. For example:

int a = -5; // a is -5 int b = +a; // b is -5 a = 5; // a is now 5 int c = +a; // c is 5

Notice that if a starts out positive, +a is also positive. But if a starts out negative, +a is still negative. Thus the unary + operator has no effect. I guess Java provides the unary plus operator out of a need for balance.

You can also use these operators with more complex expressions, like this:

int a = 3, b = 4, c = 5; int d = a * -(b + c); // d is -27

Here b is added to c, giving a result of 9. Then the unary minus is applied, giving a result of −9. Finally, −9 is multiplied by a giving a result of −27.

Using Increment and Decrement Operators

One of the most common operations in computer programming is adding or subtracting 1 from a variable. Adding 1 to a variable is called incrementing the variable. Subtracting 1 is called decrementing. The traditional way to increment a variable is like this:

a = a + 1;

Here the expression a + 1 is calculated, and the result is assigned to the variable a.

Java provides an easier way to do this type of calculation: the increment (++) and decrement () operators. These are unary operators that apply to a single variable. Thus, to increment the variable a, you can code just this:

a++;

  Tip 

Note that an expression that uses an increment or decrement operator is a statement by itself. That's because the increment or decrement operator is also a type of assignment operator, as it changes the value of the variable it applies to.

You can only use the increment and decrement operators on variables, not on numeric literals or other expressions. For example, Java doesn't allow the following expressions:

a = b * 5++; // can't increment the number 5 a = (b * 5)++; // can't increment the expression (b * 5)

Note that you can use an increment or decrement operator in an assignment statement. Here's an example:

int a = 5; int b = a--; // both a and b are set to 4

When the second statement is executed, the expression a– is evaluated first, so a is set to 4. Then the new value of a is assigned to b. Thus both a and b are set to 4.

  TECHNICAL STAUFF 

The increment and decrement operators are unusual because they are unary operators that can be placed either before (prefix) or after (postfix) the variable they apply to. Whether you place the operator before or after the variable can have a major affect on how an expression is evaluated. If you place an increment or decrement operator before its variable, the operator is applied before the rest of the expression is evaluated. As a result, the incremented value of the variable is used in the expression. In contrast, if you place the operator after the variable, the operator is applied after the expression is evaluated. Thus the original value of the variable is used in the expression.

Confused yet? A simple example can clear it up. First, consider these statements with an expression that uses a postfix increment:

int a = 5; int b = 3; int c = a * b++; // c is set to 15

When the expression in the third statement is evaluated, the original value of b-that is, 3-is used in the multiplication. Thus c is set to 15.

Then b is incremented to 4.

Now consider this version, with a prefix increment:

int a = 5; int b = 3; int c = a * ++b; // c is set to 20

This time, b is incremented before the multiplication is performed, so c is set to 20. Either way, b ends up set to 4.

Similarly, consider this example:

int a = 5; int b = --a; // b is set to 5, a is set to 4.

This example is similar to an earlier example, but this time the prefix increment operator is used. When the second statement is executed, the value of a is assigned to b. Then a is decremented. As a result, b is set to 5, and a is set to 4.

  Warning 

Because the increment and decrement operators can be confusing when used with other operators in an expression, I suggest you use them alone. Whenever you're tempted to incorporate an increment or decrement operator into a larger expression, pull the increment or decrement out of the expression and make it a separate statement either before or after the expression. In other words, code this:

b++; c = a * b;

instead of this:

c = a * ++b;

In the first version, it's crystal-clear that b is incremented before the multiplication is done.

Using the Assignment Operator

The standard assignment operator (=) is used to assign the result of an expression to a variable. In its simplest form, you code it like this:

variable = expression;

Here's an example:

int a = (b * c) / 4;

You've already seen plenty of examples of assignment statements like this one, so I won't belabor this point any further. However, I do want to point out-just for the record-that you cannot code an arithmetic expression on the left side of an equal sign. Thus the following statement doesn't compile:

int a; a + 3 = (b * c);

  Warning 

In the rest of this section, I point out some unusual ways in which you can use the assignment operator. I don't actually recommend that you use any of these techniques, as they are rarely necessary and almost always confusing. However, knowing about them can shed light on how Java expressions work and can sometimes help you find sneaky problems in your code.

The key to understanding the rest of this section is realizing that in Java, assignments are expressions, not statements. In other words, a = 5 is an assignment expression, not an assignment statement. It becomes an assignment statement only when you add a semicolon to the end.

The result of an assignment expression is the value that's assigned to the variable. For example, the result of the expression a = 5 is 5. Likewise, the result of the expression a = (b + c) * d is the result of the expression (b + c) * d.

The implication is that you can use assignment expressions in the middle of other expressions. For example, the following is legal:

int a; int b; a = (b = 3) * 2; // a is 6, b is 3

As in any expression, the part of the expression inside the parentheses is evaluated first. Thus, b is assigned the value 3. Then the multiplication is performed, and the result (6) is assigned to the variable a.

Now consider a more complicated case:

int a; int b = 2; a = (b = 3) * b; // a is 9, b is 3

What's happening here is that the expression in the parentheses is evaluated first, which means that b is set to 3 before the multiplication is performed.

The parentheses are important in the previous example because without parentheses, the assignment operator is the last operator to be evaluated in Java's order of precedence. Consider one more example:

int a; int b = 2; a = b = 3 * b; // a is 6, b is 6

This time, the multiplication 3 * b is performed first, giving a result of 6. Then, this result is assigned to b. Finally, the result of that assignment expression (6) is assigned to a.

Incidentally, the following expression is also legal:

a = b = c = 3;

This expression assigns the value 3 to all three variables. Although this code seems pretty harmless, you're better off just writing three assignment statements. (You might guess that clumping the assignments together is more efficient than writing them on three lines, but you'd be wrong. These three assignments require the same number of bytecode instructions either way.)

Using Compound Assignment Operators

A compound assignment operator is an operator that performs a calculation and an assignment at the same time. All of Java's binary arithmetic operators (that is, the ones that work on two operands) have equivalent compound assignment operators. Table 3-2 lists them.

Table 3-2: Compound Arithmetic Operators

Open table as spreadsheet

Operator

Description

+=

Addition and assignment

-=

Subtraction and assignment

*=

Multiplication and assignment

/=

Division and assignment

%=

Remainder and assignment

For example, this statement

a += 10;

is equivalent to

a = a + 10;

and this statement

z *=2;

is equivalent to

z = z * 2;

  Warning 

To avoid confusion, compound assignment expressions are best used by themselves, not in combination with other expressions. For example, consider these statements:

int a = 2; int b = 3; a *= b + 1;

Is a set to 7 or 8?

In other words, is the third statement equivalent to

a = a * b + 1; // This would give 7 as the result

or

a = a * (b + 1); // This would give 8 as the result

At first glance, you might expect the answer to be 7, because multiplication has a higher precedence than addition. But assignment has the lowest precedence of all, and the multiplication here is performed as part of the assignment. As a result, the addition is performed before the multiplication-and the answer is 8. (Gotcha!)

Using the Math Class

Java's built-in operators are useful, but they don't come anywhere near providing all the mathematical needs of most Java programmers. That's where the Math class comes in. It includes a bevy of built-in methods that perform a wide variety of mathematical calculations, from basic functions such as calculating an absolute value or a square root to trigonometry functions such as sin and cos, to practical functions such as rounding numbers or generating random numbers.

  Tip 

I was going to make a joke here about having to take a Math class to fully appreciate the Math class, or how you'd better stay away from the Math class if you didn't do so well in Math class, or how if you're on the football team, maybe you can get someone to do the Math class for you. But it seemed too easy, so I decided not to.

  TECHNICAL STAUFF 

All the methods of the Math class are declared as static methods, which means you can use them by specifying the class name Math followed by a period and a method name. For example, here's a statement that calculates the square root of a number stored in a variable named y:

double x = Math.sqrt(y);

The Math class is contained in the java.lang package, which is automatically available to all Java programs. As a result, you don't have to provide an import statement to use the Math class.

The following sections describe the most useful methods of the Math class.

Constants of the Math class

The Math class defines two constants that are useful for many mathematical calculations. Table 3-3 lists these constants.

Table 3-3: Constants of the Math Class

Open table as spreadsheet

Constant

What It Is

Value

PI

The constant Pi (π), the ratio of a circle's radius and diameter

3.141592653589793

E

The base of natural logarithms

2.718281828459045

Note that these constants are only approximate values, because both p and e are irrational numbers.

The program shown in Listing 3-2 illustrates a typical use of the constant PI. Here, the user is asked to enter the radius of a circle. The program then calculates the area of the circle in line 13. (The parentheses aren't really required in the expression in this statement, but they help clarify that the expression is the Java equivalent to the formula for the area of a circle, pr2.)

Here's the console output for a typical execution of this program, in which the user entered 5 as the radius of the circle:

Welcome to the circle area calculator. Enter the radius of your circle: 5 The area is 78.53981633974483

Listing 3-2: The Circle Area Calculator

import java.util.Scanner; public class CircleAreaApp { static Scanner sc = new Scanner(System.in); public static void main(String[] args) { System.out.println( "Welcome to the circle area calculator."); System.out.print("Enter the radius of your circle: "); double r = sc.nextDouble(); double area = Math.PI * (r * r); →13 System.out.println("The area is " + area); } }

Mathematical functions

Table 3-4 lists the basic mathematical functions that are provided by the Math class. As you can see, you can use these functions to calculate such things as the absolute value of a number, the minimum and maximum of two values, square roots, powers, and logarithms.

Table 3-4: Mathematical Functions Provided by the Math Class

Open table as spreadsheet

Method

Explanation

abs(argument)

Returns the absolute value of the argument. The argument can be an int, long, float, or double. The return value is the same type as the argument.

cbrt(argument)

Returns the cube root of the argument. The argument and return value are doubles.

exp(argument)

Returns e raised to the power of the argument. The argument and the return value are doubles.

hypot(arg1, arg2)

Returns the hypotenuse of a right triangle calculated according to the Pythagorean theorem-the square root of x2+ y2. The argument and the return values are doubles.

log(argument)

Returns the natural logarithm (base e) of the argument. The argument and the return value are doubles.

log10(argument)

Returns the base 10 logarithm of the argument. The argument and the return value are doubles.

max(arg1, arg2)

Returns the larger of the two arguments. The arguments can be int, long, float, or double, but both must be of the same type. The return type is the same type as the arguments.

min(arg1, arg2)

Returns the smaller of the two arguments. The arguments can be int, long, float, or double, but both must be of the same type. The return type is the same type as the arguments.

pow(arg1, arg2)

Returns the value of the first argument raised to the power of the second argument. Both arguments and the return value are doubles.

random()

Returns a random number that's greater than or equal to 0.0 but less than 1.0. This method doesn't accept an argument, but the return value is a double.

signum(argument)

Returns a number that represents the sign of the argument: −1.0 if the argument is negative, 0.0 if the argument is zero, and 1.0 if the argument is positive. The argument can be a double or a float. The return value is the same type as the argument.

sqrt(argument)

Returns the square root of the argument. The argument and return value are doubles.

The program shown in the upcoming Listing 3-3 demonstrates each of these methods except random. When run, it produces output similar to this:

abs(b) = 50 cbrt(x) = 2.924017738212866 exp(y) = 54.598150033144236 hypot(y, z)= 5.0 log(y) = 1.0986122886681096 log10(y) = 0.47712125471966244 max(a, b) = 100 min(a, b) = -50 pow(a, c) = 1000000.0 random() = 0.8536014557793756 signum(b) = -1.0 sqrt(x) = 1.7320508075688772

You can use this output to get an idea of the values returned by these Math class methods. For example, you can see that the expression Math.sqrt(y) returns a value of 5.0 when y is 25.0.

The following paragraphs point out a few interesting tidbits concerning these methods:

Listing 3-3: A Program That Uses the Mathematical Methods of the Math Class

public class MathFunctionsApp { public static void main(String[] args) { int a = 100; int b = -50; int c = 3; double x = 25.0; double y = 3.0; double z = 4.0; System.out.println("abs(b) = " + Math.abs(b)); System.out.println("cbrt(x) = " + Math.cbrt(x)); System.out.println("exp(y) = " + Math.exp(z)); System.out.println("hypot(y, z)= " + Math.hypot(y, z)); System.out.println("log(y) = " + Math.log(y)); System.out.println("log10(y) = " + Math.log10(y)); System.out.println("max(a, b) = " + Math.max(a, b)); System.out.println("min(a, b) = " + Math.min(a, b)); System.out.println("pow(a, c) = " + Math.pow(a, c)); System.out.println("random() = " + Math.random()); System.out.println("signum(b) = " + Math.signum(b)); System.out.println("sqrt(x) = " + Math.sqrt(y)); } }

Creating random numbers

Sooner or later, you're going to want to write programs that play simple games. Almost all games have some element of chance built in to them, so you need a way to create computer programs that don't work exactly the same every time you run them. The easiest way to do that is to use the random method of the Math class, which Table 3-4 lists along with the other basic mathematical functions of the Math class.

The random method returns a double whose value is greater than or equal to 0.0 but less than 1.0. Within this range, the value returned by the random method is different every time you call it, and is essentially random.

  TECHNICAL STAUFF 

Strictly speaking, computers are not capable of generating truly random numbers. However, clever computer scientists over the years have developed ways to generate numbers that are random for all practical purposes. These numbers are called pseudorandom numbers because although they aren't completely random, they look random to most mortal human beings.

  TECHNICAL STAUFF 

Java has many different methods and classes for generating random numbers. For example, the java.util.Random class provides about ten specialized methods that generate random values. To generate a double with a value between 0.0 and 1.0, you can execute new Random().nextDouble(). In addition, the java.security.SecureRandom class provides random values for encrypting sensitive documents. And if size matters to you, the java.math.BigInteger class allows you to generate arbitrarily large random numbers (numbers with 1,000 digits, if that's what you need).

The random method generates a random double value between 0.0 (inclusive, meaning it could be 0.0) and 1.0 (exclusive, meaning it can't be 1.0). However, most computer applications that need random values need random integers between some arbitrary low value (usually 1, but not always) and some arbitrary high value. For example, a program that plays dice needs random numbers between 1 and 6, while a program that deals cards needs random numbers between 1 and 52 (53 if jokers are used).

As a result, you need a Java expression that converts the double value returned by the random function into an int value within the range your program calls for. The following code shows how to do this, with the values set to 1 and 6 for a dice-playing game:

int low = 1; // the lowest value in the range int high = 6; // the highest value in the range int rnd = (int)(Math.random() * (high - low + 1)) + low;

This expression is a little complicated, so I show you how it's evaluated step by step:

  1. The random method to get a random double value. This value is greater than 0.0 but less than 5.0.
  2. The random value is multiplied by the high end of the range minus the low end, plus 1. In this example, the high end is 6, and the low end is 1, so you now have a random number that's greater than or equal to 0.0 but less than 6.0. (It could be 5.99999999999999, but it never is 6.0.)
  3. This value is then converted to an integer by the (int) cast. You now have an integer that's either 0, 1, 2, 3, 4, or 5. (Remember that when you cast a double to an int, any fractional part of the value is simply discarded. Because the number is less than 6.0, it never truncates to 6.0 when it is cast to an int.)
  4. The low value in the range is now added to the random number. Assuming the low is 1, the random number is now 1, 2, 3, 4, 5, or 6. That's just what you want: a random number between 1 and 6.

To give you an idea of how this random number calculation works, Listing 3-4 shows a program that places this calculation in a method called randomInt and then calls it to simulate 100 dice rolls. The randomInt method accepts two parameters representing the low and high ends of the range, and it returns a random integer within the range. In the main method of this program, the randomInt method is called 100 times, and each random number is printed by a call to System.out.print.

The console output for this program looks something like this:

Here are 100 random rolls of the dice: 4 1 1 6 1 2 6 6 6 6 5 5 5 4 5 4 4 1 3 6 1 3 1 4 4 3 3 3 5 6 5 6 6 3 5 2 2 6 3 3 4 1 2 2 4 2 2 4 1 4 3 6 5 5 4 4 2 4 1 3 5 2 1 3 3 5 4 1 6 3 1 6 5 2 6 6 3 5 4 5 2 5 4 5 3 1 4 2 5 2 1 4 4 4 6 6 4 6 3 3

However, every time you run this program, you see a different sequence of 100 numbers.

The program shown in Listing 3-4 uses several Java features you haven't seen yet.

Listing 3-4: Rolling the Dice

public class DiceApp { public static void main(String[] args) { int roll; String msg = "Here are 100 random rolls of the dice:"; System.out.println(msg); for (int i=0; i<100; i++) → 8 { roll = randomInt(1, 6); → 10 System.out.print(roll + " "); → 11 } System.out.println(); } public static int randomInt(int low, int high) → 16 { int result = (int)(Math.random() → 18 * (high - low + 1)) + low; return result; → 20 } }

The following paragraphs explain how the program works, but don't worry if you don't get all of the elements in this program. The main thing to see is the expression that converts the random double value returned by the Math.double method to an integer.

8

The for statement causes the statements in its body (lines 10 and 11) to be executed 100 times. Don't worry about how this statement works for now; you find out about it in Book II, Chapter 5.

10

This statement calls the randomInt method, specifying 1 and 6 as the range for the random integer to generate. The resulting random number is assigned to the roll variable.

11

The System.out.print method is used to print the random number followed by a space. Because this statement calls the print method rather than the println method, the random numbers are printed on the same line rather than on separate lines.

16

The declaration for the randomInt method indicates that the method returns an int value and accepts two int arguments, one named low, the other named high.

18

This expression converts the random double value to an integer between low and high.

20

The return statement sends the random number back to the statement that called the randomInt method.

Open table as spreadsheet

Rounding functions

The Math class has four methods that round or truncate float or double values. Table 3-5 lists these methods. As you can see, each of these methods uses a different technique to calculate an integer value that's near the double or float value passed as an argument. Note that even though all four of these methods rounds a floating-point value to an integer value, only the round method actually returns an integer type (int or long, depending on whether the argument is a float or a double). The other methods return doubles that happen to be integer values.

Table 3-5: Rounding Functions Provided by the Math Class

Open table as spreadsheet

Method

Explanation

ceil(argument)

Returns the smallest double value that is an integer and is greater than or equal to the value of the argument.

floor(argument)

Returns the largest double value that is an integer and is less than or equal to the value of the argument.

rint(argument)

Returns the double value that is an integer and is closest to the value of the argument. If two integer values are equally close, returns the one that is even. If the argument is already an integer, returns the argument value.

round(argument)

Returns the integer that is closest to the argument. If the argument is a double, returns a long. If the argument is a float, returns an int.

Listing 3-5 shows a program that uses each of the four methods to round three different double values: 29.4, 93.5, and −19.3. Here's the output from this program:

round(x) = 29 round(y) = 94 round(z) = -19 ceil(x) = 30.0 ceil(y) = 94.0 ceil(z) = -19.0 floor(x) = 29.0 floor(y) = 93.0 floor(z) = -20.0 rint(x) = 29.0 rint(y) = 94.0 rint(z) = -19.0

Note that each of the four methods produces a different result for at least one of the values:

Listing 3-5: A Program That Uses the Rounding Methods of the Math Class

public class RoundingApp { public static void main(String[] args) { double x = 29.4; double y = 93.5; double z = -19.3; System.out.println("round(x) = " + Math.round(x)); System.out.println("round(y) = " + Math.round(y)); System.out.println("round(z) = " + Math.round(z)); System.out.println(); System.out.println("ceil(x) = " + Math.ceil(x)); System.out.println("ceil(y) = " + Math.ceil(y)); System.out.println("ceil(z) = " + Math.ceil(z)); System.out.println(); System.out.println("floor(x) = " + Math.floor(x)); System.out.println("floor(y) = " + Math.floor(y)); System.out.println("floor(z) = " + Math.floor(z)); System.out.println(); System.out.println("rint(x) = " + Math.rint(x)); System.out.println("rint(y) = " + Math.rint(y)); System.out.println("rint(z) = " + Math.rint(z)); } }

Formatting Numbers

Most of the programs you've seen so far have used the System.out. println or System.out.print method to print the values of variables that contain numbers. When you pass a numeric variable to one of these methods, the variable's value is converted to a string before it's printed. The exact format used to represent the value isn't very pretty. For example, large values are printed without any commas. And all the decimal digits for double or float values are printed, whether you want them to or not.

In many cases, you want to format your numbers before you print them. For example, you might want to add commas to large values and limit the number of decimal places printed. Or, if a number represents a monetary amount, you might want to add a dollar sign (or whatever currency symbol is appropriate for your locale). To do that, you can use the NumberFormat class. Table 3-6 lists the NumberFormat class methods.

  Warning 

Like many aspects of Java, the procedure for using the NumberFormat class is a little awkward. It's designed to be efficient for applications that need to format a lot of numbers, but it's overkill for most applications.

Table 3-6: Methods of the NumberFormat Class

Open table as spreadsheet

Method

Explanation

getCurrencyInstance()

A static method that returns a NumberFormat object that formats currency values.

getPercentInstance()

A static method that returns a NumberFormat object that formats percentages.

getNumberInstance()

A static method that returns a NumberFormat object that formats basic numbers.

format(number)

Returns a string that contains the formatted number.

setMinimumFractionDigits(int)

Sets the minimum number of digits to display to the right of the decimal point.

setMaximumFractionDigits(int)

Sets the maximum number of digits to display to the right of the decimal point.

The procedure for using the NumberFormat class to format numbers takes a little getting used to. First, you must call one of the static getXxxInstance methods to create a NumberFormat object that can format numbers in a particular way. Then, if you want, you can call the setMinimumFractionDigits or setMaximumFractionDigits method to set the number of decimal digits to be displayed. Finally, you call that object's format method to actually format a number.

Note that the NumberFormat class is in the java.text package, so you must include the following import statement at the beginning of any class that uses NumberFormat:

import java.text.NumberFormat;

Here's an example that uses the NumberFormat class to format a double value as currency:

double salesTax = 2.425; NumberFormat cf = NumberFormat.getCurrencyInstance(); System.out.println(cf.format(salesTax));

When you run this code, the following line is printed to the console:

$2.43

Note that the currency format rounds the value from 2.425 to 2.43.

Here's an example that formats a number using the general number format, with exactly three decimal places:

double x = 19923.3288; NumberFormat nf = NumberFormat.getNumberInstance(); nf.setMinimumFractionDigits(3); nf.setMaximumFractionDigits(3); System.out.println(nf.format(x));

When you run this code, the following line is printed:

19,923.329

Here the number is formatted with a comma, and the value is rounded to three places.

Here's an example that uses the percentage format:

double grade = .92; NumberFormat pf = NumberFormat.getPercentInstance(); System.out.println(pf.format(grade));

When you run this code, the following line is printed:

92%

  Tip 

If your program formats several numbers, consider creating the NumberFormat object as a class variable. That way, the NumberFormat object is created once when the program starts. Then, you can use the NumberFormat object from any method in the program's class. Here's a simple example that shows how this works:

import java.text.NumberFormat; public class NumberFormatClassApp { static NumberFormat cf = NumberFormat.getCurrencyInstance(); public static void main(String[] args) { printMyAllowance(); printCostOfPaintBallGun(); } public static void printMyAllowance() { double myAllowance = 5.00; cf = NumberFormat.getCurrencyInstance(); System.out.println("My allowance: " + cf.format(myAllowance)); } public static void printCostOfPaintBallGun() { double costOfPaintBallGun = 69.95; cf = NumberFormat.getCurrencyInstance(); System.out.println("Cost of Paint Ball Gun: " + cf.format(costOfPaintBallGun)); } }

Here the cf variable is created as a class variable. Then both the printMyAllowance and printCostOfPaintBallGun methods can use it.

Weird Things about Java Math

Believe it or not, computers-even the most powerful ones-have certain limitations when it comes to performing math calculations. These limitations are usually insignificant, but sometimes they sneak up and bite you. The following sections describe the things you need to watch out for when doing math in Java.

Integer overflow

  Warning 

The basic problem with integer types is that they have a fixed size. As a result, the number has a size limit that can be stored in a short, int, or long variable. Although long variables can hold numbers that are huge, sooner or later you come across a number that's too big to fit in even a long variable.

Okay, consider this (admittedly contrived) example:

int a = 1000000000; System.out.println(a); a += 1000000000; System.out.println(a); a += 1000000000; System.out.println(a); a += 1000000000; System.out.println(a);

Here you expect the value of a to get bigger after each addition. But here's the output that's displayed:

1000000000 2000000000 -1294967296 -294967296

The first addition seems to work, but after that, the number becomes negative! That's because the value has reached the size limit of the int data type. Unfortunately, Java doesn't tell you that this error has happened. It simply crams the int variable as full of bits as it can, discards whatever bits don't fit, and hopes you don't notice. Because of the way int stores negative values, large positive values suddenly become large negative values.

The moral of the story is that if you're working with large integers, you should use long rather than int because long can store much larger numbers than int. If your programs deal with numbers large enough to be a problem for long, consider using floating-point types instead. As you see in the next section, floating-point types can handle even larger values than long, and they let you know when you exceed their capacity.

Floating point weirdness

  Warning 

Floating-point numbers have problems of their own. For starters, floating-point numbers are stored using the binary number system (base 2), but humans work with numbers in the decimal number system (base 10). Unfortunately, accurately converting numbers between these two systems is sometimes impossible. That's because in any number base, certain fractions can't be represented exactly. For example, base 10 has no way to exactly represent the fraction 1/3. You can approximate it as 0.3333333, but eventually you reach the limit of how many digits you can store, so you have to stop. In base 2, it happens that one of the fractions you can't accurately represent is the decimal value 1/10. In other words, a float or double variable can't accurately represent 0.1.

Don't believe me? Try running this code:

float x = 0.1f; NumberFormat nf = NumberFormat.getNumberInstance(); nf.setMinimumFractionDigits(10); System.out.println(nf.format(x));

The resulting output is this:

0.1000000015

Although 0.1000000015 is close to 0.1, it isn't exact.

  Warning 

In most cases, Java's floating-point math is close enough not to matter. The margin of error is extremely small. If you're using Java to measure the size of your house, you'd need an electron microscope to notice the error. However, if you're writing applications that deal with financial transactions, normal rounding can sometimes magnify the errors to make them significant. You might charge a penny too much or too little sales tax. And, in extreme cases, your invoices might actually have obvious addition errors.

I'll have much more to say about floating-point numbers in Bonus Chapter 1 on this book's Web site. For now, just realize that you can't use float or double to represent money unless you don't care whether or not your books are in balance.

  Warning 

Of course, integer types are stored in binary too. But integers aren't subject to the same errors that floating-point types are-because integers don't represent fractions at all. So you don't have to worry about this type of error for integer types.

Dividing by zero

According to the basic rules of mathematics, you can't divide a number by zero. The reason is simple: Division is the inverse of multiplication-which means that if a * b = c, then it is also true that a = c/b. If you were to allow b to be zero, division would be meaningless because any number times zero is zero. Therefore both a and c would also have to be zero. In short, mathematicians solved this dilemma centuries ago by saying that division by zero is simply not allowed.

So what happens if you do attempt to divide a number by zero in a Java program? The answer depends on whether you're dividing integers or floating-point numbers. If you're dividing integers, the statement that attempts the division by zero chokes up what is called an exception, which is an impolite way of crashing the program. In Book II, Chapter 8, you find out how to intercept this exception to allow your program to continue. But in the meantime, any program you write that attempts an integer division by zero crashes.

If you try to divide a floating-point type by zero, the results are not so abrupt. Instead, Java assigns the floating-point result one of the special values listed in Table 3-7. The following paragraphs explain how these special values are determined:

  REMEMBER 

Floating-point zeros can be positive or negative. Java considers positive and negative zeros to be equal numerically.

If you attempt to print a floating-point value that has one of these special values, Java converts the value to an appropriate string. For example, suppose you execute the following statements:

double i = 50.0; double j = 0.0; double k = i / j; System.out.println(k);

The resulting console output is

Infinity

If i were −50.0, the console would display −Infinity. And if i were zero, the console would display NaN.

Table 3-7: Special Constants of the float and double Classes

Open table as spreadsheet

Constant

Meaning

POSITIVE_INFINITY

Positive infinity

NEGATIVE_INFINITY

Negative infinity

NaN

Not a number

  TECHNICAL STAUFF 

The following paragraphs describe some final bits of weirdness I want to sneak in before closing this chapter:

  • NaN is not equal to itself, which can have some strange consequences. For example:

    double x = Math.sqrt(-50); // Not a number double y = x; if (x == y) System.out.println("x equals y");

    Okay, I know I jumped the gun here on the if statement, because I don't cover if statements until Book II, Chapter 4. So just assume for the sake of argument that the if statement tests whether the variable x is equal to the variable y. Because this test follows immediately after an assignment statement that assigns the value of x to y, you can safely assume that x equals y, right?

    Wrong. Because x is NaN, y also is NaN. And NaN is never considered to be equal to any other value, including another NaN. Thus, the comparison in the if statement fails.

  • Another strange consequence: You can't assume that a number minus itself is always zero. Consider this statement:

    double z = x - x; // not necessarily zero

    Shouldn't this statement always set z to zero? Not if x is NaN. In that case, not a number minus not a number is still not a number.

  • One more, and then I'll stop: Any mathematical operation involving infinity results in either another infinity or Not a Number. For example, infinity + 5 still equals infinity. So Buzz Lightyear's call to "Infinity and beyond" just isn't going to happen. But infinity minus infinity gives you … Not a Number.

Категории