Skip to main content
Logo image

Java, Java, Java: Object-Oriented Problem Solving, 2022E

Section 5.5 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 fractional 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.5.1.

Subsection 5.5.1 Primitive Numeric Types

Table 5.5.1. Java’s numeric types
Type Bits Range of Values
byte  8 \(-128 to +127\)
short 16 \(-32768 \hbox{ to } 32767\)
int 32 \(-2147483648 \hbox{ to } 2147483647\)
long 64 \(-2^{63} \hbox{ to } 2^{63}-1\)
float 32 \(-3.40292347E+38 \hbox{ to } +3.40292347E+38\)
double 64 \(-1.79769313486231570E+308 \hbox{ to } +1.79769313486231570E+308\)
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.

Definition 5.5.2.

An n-bit quantity can represent \(2^n\) different values.
As illustrated in Table 5.5.1, the various integer types represent positive or negative whole numbers. Perhaps the most commonly used integer type in Java is the int type, which is represented in 32 bits. This means that Java can represent \(2^{32}\) different int values, which range from \(-2{,}147{,}483{,}648\) to \(2{,}147{,}483{,}647\text{,}\) that is, from \(-2^{31}\) to \((2^{31} -1)\text{.}\) Similarly, an 8-bit integer, a byte, can represent \(2^8\) or 256 different values, ranging from \(-128\) to \(+127\text{.}\) A 16-bit integer, a short, can represent \(2^{16}\) different values, which range from \(-32768\) to \(32767\text{.}\) And a 64-bit integer, a long, can represent whole number values ranging from \(-2^{63}\) to \(2^{63}-1\text{.}\)
For floating-point numbers, a 32-bit float type can represent \(2^{32}\) different real numbers and a 64-bit double value can represent \(2^{64}\) 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 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 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 example, because Java uses binary numbers to represent its numeric types, one number that cannot be represented exactly is \(\frac{1}{10}\text{.}\) 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.
Another source of problems in dealing with numeric data is due to limits 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 of 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.

Exercises Self-Study Exercises

1. Fill-In Binary.
List all of the binary values that can be represented in 4 bits---that is, all values in the range 0000 to 1111 in order separated by commas:
Hint.
Start from 0000, 0001, ...
2. 6-bit Values.
If a 6-bit representation were used for an integer type, how many different integers could be represented?
Hint.
With 3 bits, you can represent 3^2 = 32 values.
3. Data Type.
If you were writing a program to process scientific data that had to be accurate to at least 12 significant (decimal) digits, what type of data would you use?
Hint.
Some data types you could use are byte, short, int, double, and long

Subsection 5.5.2 Arithmetic Operators

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 multiplication symbol is * and not the ×. The arithmetic operators are binary operators, meaning that they each take two operands. Table 5.5.5 compares expressions involving the Java operators with their standard algebraic counterparts.
Table 5.5.5. The standard arithmetic operators arithmetic operators in Java.
Operation Operator Java Algebra
Addition \(+\) \(x + 2\) \(x + 2\)
Subtraction \(-\) \(m - 2\) \(m - 2\)
Multiplication * \(m\) * \(2\) \(2m \hbox{ or } m \times 2\)
Division / \(x / y\) \(x \div y \hbox{ or } \frac{x}{y}\)
Modulus % \(x \% y\) \(x \hbox{ modulo } y\) (for integers \(x\) and \(y\))
Although these operations should seem familiar, there are some important differences between their use in algebra and their use in a Java program. Consider the following list of expressions:
3 / 2     ==>  value 1     An integer result
3.0 / 2.0 ==>  value 1.5   A floating-point result
3 / 2.0   ==>  value 1.5   A floating-point result
3.0 / 2   ==>  value 1.5   A floating-point result
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) is 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.
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 different 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 expression (7 % 5) gives the remainder after dividing 7 by 5 — that is, 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   ==> 7 mod 5 equals 2
 5 % 7   ==> 5 mod 7 equals 5
-7 % 5   ==> -7 mod 5 equals -2
 7 % -5  ==> 7 mod -5 equals 2
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 \div 5\text{,}\) 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\text{.}\) When you do long division on \(7 \div -5\text{,}\) 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\text{.}\)
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:
if (N % 2 == 0)
    System.out.println(N + " is even");
More generally, we could use the mod operator to define divisibility by 3, 4, 10, or by any number.

Activity 5.5.1.

Try the expressions containing the % operator below to see how they can be used to check for even or odd numbers. All even numbers are divisible (with no remainder) by 2. Add another if statement that checks whether age2 is even using the % operator. Then, test if it’s divisible by 3.

Exercises Self-Study Exercises

1. Evaluate div and mod.
2. Evaluate Decimal Expressions.

Subsection 5.5.3 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 one arithmetic operand is an integer and one is a floating-point number, 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 example, 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 whenever it is possible to perform the promotion without loss of information. Because 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: whenever an expression involves operands of different types, some operands must be converted before the expression can be evaluated. Consider the following example:
byte n = 125;
short m = 32000;
n * m;
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 short s. 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.9 summarizes the actual promotion rules used by Java in evaluating 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 represented as int types in Java.
Table 5.5.9. Java promotion rules for mixed arithmetic operators. If two rules apply, choose the one that occurs first in this table.
If either operand is The other is promoted to
double double
float float
long long
byteorshort int

Exercises Self-Study Exercises

1. Evaluate Type Promotion.

Subsection 5.5.4 Operator Precedence

Table 5.5.10. Precedence order of the arithmetic operators.
Precedence Order Operator Operation
1 \(( \; )\) Parentheses
2 \(\ast \;\; / \;\; \%\) Multiplication, Division, Modulus
3 \(+ \;\; -\) Addition, Subtraction
The built-in precedence order for arithmetic operators is shown in Table 5.5.10. Parenthesized expressions have highest precedence and are evaluated 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 multiplication would be done first, even if it occurs to the right of the plus sign. Operators at the same level in the precedence hierarchy are evaluated from left to right. For example, consider the following expression:
9 + 6 - 3 * 6 / 2
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:
Step 1.  ( (9 + 6) - ((3 * 6) / 2 ) )
Step 2.  ( (9 + 6) - (18 / 2 ) )
Step 3.  ( (9 + 6) - 9 )
Step 4.  ( 15 - 9 )
Step 5.  6
Parentheses can (and should) always be used to clarify the order of operations in an expression. For example, addition will be performed before multiplication in the following expression:
(a + b) * c
Another reason to use parentheses is that Java’s precedence and promotion rules will sometimes lead to expressions that look fine but contain subtle errors. For example, consider the following expressions:
System.out.println(5/3/2.0);   // 0.5
System.out.println(5/(3/2.0)); // 3.33
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.

Exercises Self-Study Exercises

1. Evaluate Expressions.
2. Evaluate Expressions.

Subsection 5.5.5 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:
int k;
k = k + 1;// Add 1 to k and assign the result back to k
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 conjunction with other operators, there is a significant difference between preincrement and postincrement. For example, in the following code segment,
int j = 0, k = 0;   // Initially both j and k are 0
j = ++k;            // Final values of both j and k are 1
the variable k is incremented before its value is assigned to j. After execution of the assignment statement, j will equal 1 and k will equal 1. The sequence is equivalent to
int j = 0, k = 0;   // Initially both j and k are 0
k = k + 1;
j = k;              // Final values of both j and k are 1
However, in the following example,
int i = 0, k = 0; // Initially both i and k are 0
i = k++;          // Final value of i is 0 and k is 1
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
int i = 0, k = 0;   // Initially both i and k are 0
i = k;
k = k + 1;          // Final value of i is 0 and k is 1
In addition to the increment operator, Java also supplies the decrement operator \(--\), 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.5.12 summarizes the increment and decrement operators. The unary increment and decrement operators have higher precedence than any of the binary arithmetic operators.
Table 5.5.12. Java’s increment and decrement operators
Expression Operation Interpretation
\(j = ++k\) Preincrement \(k=k+1;j=k;\)
\(j = k++\) Postincrement \(j=k;k=k+1;\)
\(j = --k\) Predecrement \(k=k-1;j=k;\)
\(j = k--\) Postdecrement \(j=k;k=k-1;\)

Exercises Self-Study Exercises

1. Evaluate Increment/Decrement Expressions.
Solution.
  • k==5, j==5
  • >k==5, j==6
  • k==6, j==6
  • k==5, j==4
  • k==4, j==4

Subsection 5.5.6 Assignment Operators

In addition to the simple assignment operator (=), Java supplies a number of shortcut assignment operators that allow you to combine an arithmetic 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
k += 3;
is equivalent to the statement
k = k + 3;
Similarly, the statement
r += 3.5 + 2.0 * 9.3 ;
is equivalent to
r = r + (3.5 + 2.0 * 9.3);  // i.e., r = r + 22.1;
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.5.15 lists the other assignment operators that can be used in combination with the arithmetic operators. For each of these operations, the interpretation is the same: Evaluate the expression on the right-hand side of the operator and then perform the arithmetic operation (such as addition or multiplication) to the current value of the variable on the left of the operator.
Table 5.5.15. Java’s assignment operators assignment operators
Operator Operation Example Interpretation
\(=\) Simple assignment \(m = n;\) \(m = n;\)
\(+\!=\) Addition then assignment \(m\ +\!=\ 3;\) \(m = m + 3;\)
\(-\!=\) Subtraction then assignment \(m\ -\!=\ 3;\) \(m = m - 3;\)
\(\ast \!=\) Multiplication then assignment \(m\ \ast \!=\ 3;\) \(m = m\,\ast \,3;\)
\(/\!=\) Division then assignment \(m\ /\!=\ 3;\) \(m = m / 3;\)
\(\%\!=\) Remainder then assignment \(m\ \%\!=\ 3;\) \(m = m \% 3;\)

Activity 5.5.2.

Run the code below to see what the ++ and shorcut operators do. Click on the Show Code Lens button to trace through the code and the variable values change in the visualizer.

Exercises Self-Study Exercises

1. Evaluate Shortcut Expressions.
2.
Write four different statements that add 1 to the variable k (which has been declared as an int).

Subsection 5.5.7 Relational Operators

There are several relational operations that can be performed on numbers: \(\lt\text{,}\) \(>\text{,}\) \(\lt =\text{,}\) \(> =\text{,}\) \(= =\text{,}\) and \(!\!=\text{.}\) These correspond to the algebraic operators \(\lt\text{,}\) \(>\text{,}\) \(\leq\text{,}\) \(\geq\text{,}\) \(=\text{,}\) and \(\neq\text{.}\) Each of these operators takes two operands (integer or real) and returns a boolean result. They are defined in Table 5.5.16.
Table 5.5.16. Relational operators.
Operator Operation Java Expression
\(\lt\) Less than \(5 \lt 10\)
\(>\) Greater than \(10 > 5\)
\(\lt =\) Less than or equal to \(5 \lt = 10\)
\(> =\) Greater than or equal to \(10 > = 5\)
\(= =\) Equal to \(5 = = 5\)
\(!\!=\) Not equal to \(5\ !\!=\ 4\)
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 (\(\lt\)=), greater than or equal to (\(>\)=), and not equal to (!=) require two symbols, instead of the familiar \(\leq\text{,}\) \(\geq\text{,}\) and \(\neq\) from algebra. In each case, the two symbols should be consecutive. It is an error in Java for a space to appear between the \(\lt\) and = in \(\lt =\text{.}\)

Activity 5.5.3.

Run the code below and try changing the value of x to get each of the three possible lines in the conditional to print.
Among the relational operators, the inequalities (\(\lt\text{,}\) \(>\text{,}\) \(\lt =\text{,}\) 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 involves both arithmetic and relational operators, the arithmetic operations are done first. Table 5.5.18 includes all of the numeric operators introduced so far.
Table 5.5.18. Numeric operator precedence including relations
Precedence Order Operator Operation
1 \((\,)\) Parentheses
2 \(+\!+\ \ --\) Increment, decrement
3 \(\ast \ \ /\ \ \%\) Multiplication, division, modulus
4 \(+\ \ -\) Addition, subtraction
5 \(\lt \,\,\,\,>\ \ \lt =\ \ > =\) Relational operators
6 \(= =\ \ !\!=\) Equality operators
To take an example, let us evaluate the following complex expression:
9 + 6 <= 25 * 4 + 2
To clarify the implicit operator precedence, we first parenthesize the expression
( 9 + 6 ) <= ( (25 * 4 ) + 2 )
and then evaluate it step by step:
Step 1. ( 9 + 6 ) <= ( (25 * 4 ) + 2 )
Step 2. ( 9 + 6 ) <= ( 100 + 2 )
Step 3. 15 <= 102
Step 4. true
The following expression is an example of an ill-formed expression:
9 + 6 <= 25 * 4 == 2
That the expression is ill formed becomes obvious if we parenthesize it and then attempt to evaluate it:
Step 1. ( ( 9 + 6 )  <= ( 25 * 4 ) ) == 2
Step 2. ( 15  <= 100 ) == 2
Step 3. true == 2      // Syntax error results here
The problem here is that the expression true == 2 is an attempt to compare 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.

Exercises Self-Study Exercises

1. Evaluate Precedence with Increment Expressions.
2. Evaluate True/False/Illegal.
3. Evaluate True/False/Illegal.
You have attempted of activities on this page.