8. Java Data and Operators

8.4. Numeric Data and Operators

Java has two kinds of numeric data: integers, which have no fractional part, and real numbers or floating-point numbers, which contain a frac- tional component. Java recognizes four different kinds of integers: byte, short, int, and long, which are distinguished by the number of bits used to represent them. A binary digit, or bit, is a 0 or a 1. (Recall that computers read instructions as series of 0s and 1s.) Java has two different kinds of real numbers, float and double, which are also distinguished by the number of bits used to represent them. See Table 5.3.

 

TABLE 5.3 Java’s numeric types

Type BitsRange of Values

byte8128 to + 127

short1632768 to 32767

int322147483648 to 2147483647

long64263 to 263 1

float323.40292347E + 38 to + 3.40292347E + 38

double641.79769313486231570E + 308 to + 1.79769313486231570E + 308

 

,,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

J

Figure 5.2: The revised OneRowNim uses a boolean variable to keep track of who’s turn it is.

 

 

The more bits a data type has, the more values it can represent. One bit can represent two possible values, 1 and 0, which can be used to stand for true and false, respectively. Two bits can represent four possible values: 00, 01, 10, and 11; three bits can represent eight possible values: 000, 001,

 

010, 100, 101, 110, 011, 111. And, in general, an n-bit quantity can represent

2n different values.

As illustrated in Table 5.3, the various integer types represent posi- tive or negative whole numbers. Perhaps the most commonly used in- teger type in Java is the int type, which is represented in 32 bits. This means that Java can represent 232 different int values, which range from 2,147,483,648 to 2,147,483,647, that is, from231 to (2311). Similarly,

an 8-bit integer, a byte, can represent 28 or 256 different values, ranging Integer data types

from 128 to +127. A 16-bit integer, a short, can represent 216 different values, which range from 32768 to 32767. And a 64-bit integer, a long, can represent whole number values ranging from 263 to 263 1.

For floating-point numbers, a 32-bit float type can represent 232 dif- ferent real numbers and a 64-bit double value can represent 264 different real numbers.

 

It is worth noting that just as model airplanes are representations of

real airplanes, Java’s numeric types are representations or models of the Data types are abstractions

numbers we deal with in mathematics. In designing Java’s data types, various trade-offs have been made in order to come up with practical implementations.

One trade-off is that the set of integers is infinite, but Java’s int type

can only represent a finite number of values. Similarly, Java cannot Representation trade-offs

represent the infinite number of values that occur between, say, 1.111 and

1.112. So, certain real numbers cannot be represented at all. For exam- ple, because Java uses binary numbers to represent its numeric types, one number that cannot be represented exactly is 1 . This inability to exactly represent a value is known as round-off error. Being unable to represent certain values can cause problems in a program. For example, it might be

difficult to represent dollars and cents accurately in a program.Round-off error

Another source of problems in dealing with numeric data is due to lim- its in their precision. For example, a decimal number represented as a double value can have a maximum of 17 significant digits, and a float can have a maximum 8. A significant digit is one that contributes to the number’s value. If you tried to store values such as 12345.6789 or 0.123456789 in a float variable, they would be rounded off to 12345.679 and 0.12345679, respectively, causing a possible error.

 

 

SELF-STUDY EXERCISES

 

 

 

 

 

Numeric operators


Numeric Operations

The operations that can be done on numeric data include the standard algebraic operations: addition (+), subtraction ( ), multiplication (*), division (/), as well as the modulus (%) operator. Note that in Java, the multiplica- tion symbol is * and not the . The arithmetic operators are binary op- erators, meaning that they each take two operands. Table 5.4 compares

TABLE 5.4 The standard arithmetic operators in Java

 

 

 

 

Multiplication*m * 22m or 2 ×x m

Division/x/yx ÷ y or y

Modulus%x%yx modulo y (for integers x and y)

 

 

 

expressions involving the Java operators with their standard algebraic counterparts.

Although these operations should seem familiar, there are some im- portant differences between their use in algebra and their use in a Java program. Consider the following list of expressions:

,,

 

 

 

 

 

 

 

 

Integer division gives an integer


J

In each of these cases we are dividing the quantity 3 by the quantity 2. However, different results are obtained depending on the type of the operands involved. When both operands are integers, as in (3/2), the result must also be an integer. Hence, (3/2) has the value 1, an integer. Because integers cannot have a fractional part, the 0.5 is simply discarded. Integer division (/) always gives an integer result. Thus, the value of (6/2)

 

resultis 3 and the value of (7/2) is also 3. Because 3.5 is not an integer, the result of dividing 7 by 2 cannot be 3.5.

 

 

 

 

 

 

 

 

 

Modular arithmetic


On the other hand, when either operand is a real number, as in the last three cases, the result is a real number. Thus, while the same symbol (/) is used for dividing integers and real numbers, there are really two dif- ferent operations involved here: integer division and floating-point division. Using the same symbol (/) for different operations (integer division and real division) is known as operator overloading. It is similar to method overloading, which was discussed in Chapter 3.

What if you want to keep the remainder of an integer division? Java provides the modulus operator (%), which takes two operands. The ex- pression (7 % 5) gives the remainder after dividing 7 by 5—2 in this case.

 

In general, the expression (m % n) (read m mod n) gives the remainder after m is divided by n. Here are several examples:

 

 

 

 

7 %5

\J

The best way to interpret these examples is to perform long division on the operands keeping both the quotient and the remainder. For example, when you do long division on 7 5, you get a quotient of -1 and a re- mainder of -2. The quotient is the value of 7/5 and the remainder is the value of 7%5. When you do long division on 7 5, you get a quotient of -1 and a remainder of 2. The quotient is the value of 7/ 5 and the remainder is the value of 7% 5.

We will encounter many practical uses for the modulus operator in our programs. For a simple example, we use it when we want to determine whether an integer is even or odd. Numbers that leave a 0 remainder when divided by 2 are even:

,,

 

J

More generally, we could use the mod operator to define divisibility by 3, 4, 10, or by any number.

 

Numeric Promotion Rules

Java is considered a strongly typed language because all expressions in

Java, such as (3/2), have a type associated with them. In cases where Expressions have a type

one arithmetic operand is an integer and one is a floating-point num- ber, Java promotes the integer into a floating-point value and performs a floating-point operation.

Promotion is a matter of converting one type to another type. For ex- ample, in the expression (5 + 4.0), the value 5 must be promoted to 5.0 before floating-point addition can be performed on (5.0 + 4.0). Generally speaking, automatic promotions such as these are allowed in Java when- ever it is possible to perform the promotion without loss of information. Be- cause an integer (5) does not have a fractional component, no information will be lost in promoting it to a real number (5.0). On the other hand, you cannot automatically convert a real number (5.4) to an integer (5) because that might lead to loss of information. This leads to the following rule:

 

This rule is actually an instance of a more general rule, for whenever an expression involves operands of different types, some operands must be

 

converted before the expression can be evaluated. Consider the following example:

,,

 

 

 

 

 

 

 

 

 

Promotion is automatic


\J

In this case, (n * m) involves two different integer types, byte and short. Before evaluating this expression Java must first promote the byte to a short and carry out the operation as the multiplication of two shorts. Conversion of short to byte would not be possible because there’s no way to represent the value 32000 as a byte.

It is important to note that this conversion rule applies regardless of

the actual values of the operands. In applying the rule, Java looks at the operand’s type, not its value. So even if m were assigned a value that could be represented as a byte (for example, 100), the promotion would still go from smaller to larger type. This leads to following the general rule:

 

 

 

Table 5.5 summarizes the actual promotion rules used by Java in evaluat- ing expressions involving mixed operands. Note that the last rule implies that integer expressions involving byte or short or int are performed as int. This explains why integer literals—such as 56 or 108—are rep- resented as int types in Java.

 

TABLE 5.5 Java promotion rules for mixed arithmetic operators. If two rules apply, choose the one that occurs first in this table.

 

If either operand isThe other is promoted to

 

 

doubledouble

floatfloat

longlong

byte or shortint

 

Operator Precedence

The built-in precedence order for arithmetic operators is shown in Ta- ble 5.6. Parenthesized expressions have highest precedence and are evalu- ated first. Next come the multiplication, division, and modulus operators, followed by addition and subtraction. When we have an unparenthesized expression that involves both multiplication and addition, the multiplica- tion would be done first, even if it occurs to the right of the plus sign. Op-

 

 

 

 

 

 

 

+ Addition, Subtraction

 

 

erators at the same level in the precedence hierarchy are evaluated from left to right. For example, consider the following expression:

,,

 

J

In this case, the first operation to be applied will be the multiplication (*), followed by division (/), followed by addition (+), and then finally the subtraction ( ). We can use parentheses to clarify the order of evaluation. A parenthesized expression is evaluated outward from the innermost set of parentheses:

,,

 

 

 

J

Parentheses can (and should) always be used to clarify the order of oper- ations in an expression. For example, addition will be performed before multiplication in the following expression:

,,

 

J

Another reason to use parentheses is that Java’s precedence and promo- tion rules will sometimes lead to expressions that look fine but contain subtle errors. For example, consider the following expressions:

 

 

 

\J

The first gives a result of 0.5, but the use of parentheses in the second gives a result of 3.33. If the second is the expected interpretation, then the parentheses here helped avoid a subtle semantic error.

 

SELF-STUDY EXERCISE

 

Increment and Decrement Operators

Java provides a number of unary operators that are used to increment or decrement an integer variable. For example, the expression k++ uses the increment operator ++ to increment the value of the integer variable k. The expression k++ is equivalent to the following Java statements:

,,

 

 

 

 

Preincrement and postincrement


J

The unary ++ operator applies to a single integer operand, in this case to the variable k. It increments k’s value by 1 and assigns the result back to k. It may be used either as a preincrement or a postincrement operator. In the expression k++, the operator follows the operand, indicating that it is being used as a postincrement operator. This means that the increment operation is done after the operand’s value is used.

Contrast that with the expression ++k in which the ++ operator precedes its operand. In this case, it is used as a preincrement operator, which means that the increment operation is done before the operand’s value is used.

When used in isolation, there is no practical difference between k++ and ++k. Both are equivalent to k = k + 1. However, when used in con- junction with other operators, there is a significant difference between preincrement and postincrement. For example, in the following code segment,

 

 

 

 

 

 

Precedence order


\J

the variable k is incremented before its value is assigned to j. After execu- tion of the assignment statement, j will equal 1 and k will equal 1. The sequence is equivalent to

,,

 

 

 

J

However, in the following example,

,,

 

J

the variable k is incremented after its value is assigned to i. After execution of the assignment statement, i will have the value 0 and k will have the value 1. The preceding sequence is equivalent to

,,

 

 

 

 

Predecrement and postdecrement


J

In addition to the increment operator, Java also supplies the decrement op-

 

erator , which can also be used in the predecrement and postdecrement forms. The expression k will first decrement k’s value by 1 and then use k in any expression in which it is embedded. The expression k will use the current value of k in the expression in which k is contained and then it will decrement k’s value by 1. Table 5.7 summarizes the increment and decrement operators. The unary increment and decrement operators have higher precedence than any of the binary arithmetic operators.

TABLE 5.7 Java’s increment and decrement operators

 

 

 

 

j = k Postdecrementj = k; k = k 1;

 

 

 

 

SELF-STUDY EXERCISE

Assignment Operators

In addition to the simple assignment operator (=), Java supplies a num- ber of shortcut assignment operators that allow you to combine an arith- metic operation and an assignment in one operation. These operations can be used with either integer or floating-point operands. For example, the += operator allows you to combine addition and assignment into one expression. The statement

,,

 

J

is equivalent to the statement

,,

 

J

 

Similarly, the statement

,,

 

J

is equivalent to

,,

 

J

As these examples illustrate, when using the += operator, the expression on its right-hand side is first evaluated and then added to the current value of the variable on its left-hand side.

Table 5.8 lists the other assignment operators that can be used in com- bination with the arithmetic operators. For each of these operations, the interpretation is the same: Evaluate the expression on the right-hand side

 

 

 

 

 

 

=Multiplication then assignmentm = 3;m = m 3;

/ =Division then assignmentm / = 3;m = m/3;

% =Remainder then assignmentm % = 3;m = m%3;

 

of the operator and then perform the arithmetic operation (such as addi- tion or multiplication) to the current value of the variable on the left of the operator.

SELF-STUDY EXERCISES

Relational Operators

There are several relational operations that can be performed on integers:

<, >, <=, >=, ==, and ! =. These correspond to the algebraic operators

<, >,,, =, and =. Each of these operators takes two operands (integer or real) and returns a boolean result. They are defined in Table 5.9.

TABLE 5.9 Relational operators

 

Operator

Operation

Java Expression

<

Less than

5 < 10

>

Greater than

10 > 5

<=

Less than or equal to

5 <= 10

>=

Greater than or equal to

10 >= 5

==

Equal to

5 == 5

! =

Not equal to

5 ! = 4

 

 

 

Equals vs. assigns


Note that several of these relational operators require two symbols in Java. Thus, the familiar equals sign (=) is replaced in Java by ==. This is so the equality operator can be distinguished from the assignment operator.

 

Also, less than or equal to (<=), greater than or equal to (>=), and not equal to (!=) require two symbols, instead of the familiar , , and = from algebra. In each case, the two symbols should be consecutive. It is an error in Java for a space to appear between the < and = in <=.

Among the relational operators, the inequalities (<, >, <=, and >=) have higher precedence than the equality operators (== and ! =). In an expression that involves both kinds of operators, the inequalities would be evaluated first. Otherwise, the expression is evaluated from left to right.

Taken as a group the relational operators have lower precedence than the arithmetic operators. Therefore, in evaluating an expression that in- volves both arithmetic and relational operators, the arithmetic operations are done first. Table 5.10 includes all of the numeric operators introduced so far.

TABLE 5.10 Numeric operator precedence including relations

 

 

 

 

 

/ %Multiplication, division, modulus

4+Addition, subtraction

5< > <= >=Relational operators

6== ! =Equality operators

 

To take an example, let us evaluate the following complex expression:

,,

 

J

To clarify the implicit operator precedence, we first parenthesize the expression

,,

 

J

and then evaluate it step by step:

,,

 

 

 

J

The following expression is an example of an ill-formed expression:

,,

 

J

 

That the expression is ill formed becomes obvious if we parenthesize it and then attempt to evaluate it:

,,

 

 

 

 

 

Strong typing


J

The problem here is that the expression true == 2 is an attempt to com- pare an int and a boolean value, which can’t be done. As with any other binary operator, the == operator requires that both of its operands be of the same type. This is another example of Java’s strong type checking.

SELF-STUDY EXERCISES

 

 

From the Java Library java.lang.Math

THE java.lang.Math class provides many common mathematical java.sun.com/j2se/1.5.0/docs/api/ functions that will prove useful in performing numerical computations. As an element of the java.lang package, it is included implicitly in all

Java programs. Table 5.11 lists some of the most commonly used Math

class methods.

TABLE 5.11 A selection of Math class methods

Method

Description

Examples

int abs(int x) long abs(long x) float abs(float x)

Absolute value of x

if x >= 0 abs(x) is x if x < 0 abs(x) is x

int ceil(double x)

Rounds x to the smallest integer not less than x

ceil(8.3) is 9 ceil(8.3) is 8

int floor(double x)

Rounds x to the largest integer not greater than x

floor (8.9) is 8 floor(8.9) is 9

double log(double x)

Natural logarithm of x

log (2.718282) is 1.0

double pow(double x, double y)

x raised to the y power (xy)

pow(3, 4 ) is 81.0

pow(16.0, 0.5) is 4.0

double random()

Generates a random number in the interval [0,1)

random() is 0.5551

random() is 0.8712

long round(double x)

Rounds x to an integer

round(26.51) is 27

round (26.499) is 26

double sqrt(double x)

Square root of x

sqrt(4.0) is 2.0

 

All Math methods are static class methods and are, therefore, in- voked through the class name. For example, we would calculate 24 as Math.pow(2,4), which evaluates to 16. Similarly, we compute the square root of 225.0 as Math.sqrt(225.0), which evaluates to 15.0.

 

Indeed, Java’s Math class cannot be instantiated and cannot be sub- classed. Its basic definition is

,,

 

 

 

 

J

By declaring the Math class public final, we indicate that it can be accessed (public) but it cannot be extended or subclassed (final). By declaring its default constructor to be private, we prevent this class from being instantiated. The idea of a class that cannot be subclassed and can- not be instantiated may seem a little strange at first. The justification for it here is that it provides a convenient and efficient way to introduce helpful math functions into the Java language.

Defining the Math class in this way makes it easy to use its methods, because you don’t have to create an instance of it. It is also a very efficient design because its methods are static elements of the java.lang pack- age. This means they are loaded into memory at the beginning of your program’s execution, and they persist in memory throughout your pro- gram’s lifetime. Because Math class methods do not have to be loaded into memory each time they are invoked, their execution time will improve dramatically.