3.5 Arithmetic Operators: '*', '/', '%', '+', '-'

The arithmetic operators are used to construct mathematical expressions as in algebra. Their operands are of numeric type (which includes the char type).

Arithmetic Operator Precedence and Associativity

In Table 3.3, the precedence of the operators is in decreasing order, starting from the top row, which has the highest precedence. Unary subtraction has higher precedence than multiplication. The operators in the same row have the same precedence. Binary multiplication, division, and remainder operators have the same precedence. The unary operators have right associativity, and the binary operators have left associativity.

Table 3.3. Arithmetic Operators

Unary

+

Addition

-

Subtraction

  

Binary

*

Multiplication

/

Division

%

Remainder

 

+

Addition

-

Subtraction

  

Evaluation Order in Arithmetic Expressions

Java guarantees that the operands are fully evaluated from left to right before an arithmetic binary operator is applied. Of course, if evaluation of an operand causes an exception, the subsequent operands will not be evaluated.

In the expression a + b * c, the operand a will always be fully evaluated before the operand b, which will always be fully evaluated before the operand c. However, the multiplication operator * will be applied before the addition operator +, respecting the precedence rules. Note that a, b, and c can be arbitrary arithmetic expressions that have been determined to be the operands of the operators.

Range of Numeric Values

As we have seen, all numeric types have a range of valid values (Section 2.2, p. 28). This range is given by the constants named MAX_VALUE and MIN_VALUE, which are defined in each numeric wrapper class.

The arithmetic operators are overloaded, meaning that the operation of an operator varies depending on the type of its operands. Floating-point arithmetic is performed if any operand of an operator is of floating-point type, otherwise, integer arithmetic is performed.

Values that are out-of-range or are results of invalid expressions, are handled differently depending on whether integer or floating-point arithmetic is performed.

Integer Arithmetic

Integer arithmetic always returns a value that is in range, except in the case of integer division by zero and remainder by zero, which causes an ArithmeticException (see the division operator / and the remainder operator % below). A valid value does not necessarily mean that the result is correct, as demonstrated by the following examples:

int tooBig   = Integer.MAX_VALUE + 1;    // -2147483648 which is Integer.MIN_VALUE.
int tooSmall = Integer.MIN_VALUE - 1;    //  2147483647 which is Integer.MAX_VALUE.

The results above should be values that are out-of-range. However, integer arithmetic wraps if the result is out-of-range, that is, the result is reduced modulo in the range. In order to avoid wrapping of out-of-range values, programs should either use explicit checks or a wider type. If the type long is used in the examples above, the results would be correct in the long range:

long notTooBig   = Integer.MAX_VALUE + 1L;   //  2147483648L in range.
long notTooSmall = Integer.MIN_VALUE - 1L;   // -2147483649L in range.
Floating-point Arithmetic

Certain floating-point operations result in values that are out-of-range. Typically, adding or multiplying two very large floating-point numbers can result in an out-of-range value which is represented by Infinity (see Figure 3.2). Attempting floating-point division by zero also returns infinity. The examples below show how this value is printed as signed infinity.

System.out.println( 4.0 / 0.0);         // Prints:  Infinity
System.out.println(-4.0 / 0.0);         // Prints: -Infinity
Figure 3.2. Overflow and Underflow in Floating-point Arithmetic

graphics/03fig02.gif

Both positive and negative infinity represent overflow to infinity, that is, the value is too large to be represented as a double or float (see Figure 3.2). Signed infinity is represented by named constants POSITIVE_INFINITY and NEGATIVE_INFINITY, in the wrapper classes java.lang.Float and java.lang.Double. A value can be compared with these constants to detect overflow.

Floating-point arithmetic can also result in underflow to zero, that is, the value is too small to be represented as a double or float (see Figure 3.2). Underflow occurs in the following situations:

  • the result is between Double.MIN_VALUE (or Float.MIN_VALUE) and zero; for example, the result of (5.1E-324 - 4.9E-324). Underflow then returns positive zero 0.0 (or 0.0F).

  • the result is between -Double.MIN_VALUE (or -Float.MIN_VALUE) and zero; for example, the result of (-Double.MIN_VALUE * 1E-1). Underflow then returns negative zero -0.0 (or -0.0F).

Negative zero compares equal to positive zero, i.e. (-0.0 == 0.0) is true.

Certain operations have no mathematical result, and are represented by NaN (Not a Number), for example calculating the square root of -1. Another example is (floating-point) dividing zero by zero:

System.out.println(0.0 / 0.0);        // Prints: NaN

NaN is represented by the constant named NaN in the wrapper classes java.lang.Float and java.lang.Double. Any operation involving NaN produces NaN. Any comparison (except inequality !=) involving NaN and any other value (including NaN) returns false. An inequality comparison of NaN with another value (including NaN) always returns true. However, the recommended way of checking a value for NaN is to use the static method isNaN() defined in both wrapper classes, java.lang.Float and java.lang.Double.

Strict Floating-Point Arithmetic: strictfp

Although floating-point arithmetic in Java is defined in accordance with the IEEE-754 32-bit (float) and 64-bit (double) standard formats, the language does allow JVM implementations to use other extended formats for intermediate results. This means that floating-point arithmetic can give different results on such JVMs, with possible loss of precision. Such a behavior is termed non-strict, in contrast to being strict and adhering to the standard formats.

To ensure identical results are produced on all JVMs, the keyword strictfp can be used to enforce strict behavior for floating-point arithmetic. The modifier strictfp can be applied to classes, interfaces, and methods. A strictfp method ensures that all code in the method is executed strictly. If a class or interface is declared to be strictfp, then all code (in methods, initializers, and nested classes and interfaces) is executed strictly. If the expression is determined to be in a strictfp construct, it is executed strictly. However, note that strictness is not inherited by the subclasses or subinterfaces. Constant expressions are always evaluated strictly at compile time.

Unary Arithmetic Operators: -, +

The unary operators have the highest precedence of all the arithmetic operators. The unary operator - negates the numeric value of its operand. The following example illustrates the right associativity of the unary operators:

int value = - -10;              // (-(-10)) is 10

Notice the blank needed to separate the unary operators; otherwise, these would be interpreted as the decrement operator -- (see Section 3.7, p. 63). The unary operator + has no effect on the evaluation of the operand value.

How negative integers are represented using 2's complement, is discussed in Section G.4 on page 598.

Multiplicative Binary Operators: *, /, %

Multiplication Operator: *

Multiplication operator * multiplies two numbers.

int    sameSigns     = -4    * -8;        // result:  32
double oppositeSigns =  4.0  * -8.0;      // result: -32.0
int    zero          =  0    * -0;        // result:   0
Division Operator: /

The division operator / is overloaded. If its operands are integral, the operation results in integer division.

int    i1 = 4  / 5;   // result: 0
int    i2 = 8  / 8;   // result: 1
double d1 = 12 / 8;   // result: 1 by integer division. d1 gets the value 1.0.

Integer division always returns the quotient as an integer value, i.e. the result is truncated toward zero. Note that the division performed is integer division if the operands have integral values, even if the result will be stored in a floating-point type.

An ArithmeticException is thrown when attempting integer division with zero, meaning that integer division by zero is an illegal operation.

If any of the operands is a floating-point type, the operation performs floating-point division.

double d2 = 4.0 / 8;      // result: 0.5
double d3 = 8 / 8.0;      // result: 1.0
double d4 = 12.0F / 8;    // result: 1.5F

double result1 = 12.0 / 4.0 * 3.0;       // ((12.0 / 4.0) * 3.0) which is 9
double result2 = 12.0 * 3.0 / 4.0;       // ((12.0 * 3.0) / 4.0) which is 9
Remainder Operator: %

In mathematics, when we divide a number (the dividend) by a another number (the divisor), the result can be expressed in terms of a quotient and a remainder. For example, dividing 7 by 5, the quotient is 1 and the remainder is 2. The remainder operator % returns the remainder of the division performed on the operands.

int quotient  = 7 / 5;   // Integer division operation: 1
int remainder = 7 % 5;   // Integer remainder operation: 2

For integer remainder operation, where only integer operands are involved, evaluation of the expression (x % y) always satisfies the following relation:


x == (x / y) * y + (x % y)

In other words, the right-hand side yields a value that is always equal to the value of the dividend. The following examples show how we can calculate the remainder so that the above relation is satisfied:

Calculating (7 % 5):


7 == (7 / 5) * 5 + (7 % 5)
  == ( 1 ) * 5 + (7 % 5)
  == 5 + (7 % 5)
2 == (7 % 5)        i.e., (7 % 5) is equal to 2

Calculating (7 % -5):


7 == (7 / -5) * -5 + (7 % -5)
  == ( -1 ) * -5 + (7 % -5)
  == 5 + (7 % -5)
2 == (7 % -5)     i.e., (7 % -5) is equal to 2

Calculating (-7 % 5):

-7 == (-7 / 5) * 5 + (-7 % 5)
   == (  -1  ) * 5 + (-7 % 5)
   ==           -5 + (-7 % 5)
-2 ==                (-7 % 5)     i.e., (-7 % 5) is equal to -2

Calculating (-7 % -5):

-7 == (-7 / -5) * -5 + (-7 % -5)
   == (   1   ) * -5 + (-7 % -5)
   ==             -5 + (-7 % -5)
-2 ==                  (-7 % -5)  i.e., (-7 % -5) is equal to -2

The above relation shows that the remainder can only be negative if the dividend is negative, the sign of the divisor is irrelevant. A short-cut to evaluating the remainder involving negative operands is the following: ignore the signs of the operands, calculate the remainder, and negate the remainder if the dividend is negative.

int  r0 =  7  %  7;     //  0
int  r1 =  7  %  5;     //  2
long r2 =  7L % -5L;    //  2L
int  r3 = -7  %  5;     // -2
long r4 = -7L % -5L;    // -2L
boolean relation = -7L == (-7L / -5L) * -5L + r4;  // true

An ArithmeticException is thrown when attempting integer remainder operation with zero.

Note that the remainder operator not only accepts integral operands, but floating-point operands as well. The floating-point remainder r is defined by the relation:


r == a - (b * q)

where a and b are the dividend and the divisor, respectively, and q is the integer quotient of (a/b). The following examples illustrate a floating-point remainder operation:

double  dr0 =  7.0  %  7.0;    //  0.0
float   fr1 =  7.0F %  5.0F;   //  2.0F
double  dr1 =  7.0  % -5.0;    //  2.0
float   fr2 = -7.0F %  5.0F;   // -2.0F
double  dr2 = -7.0  % -5.0;    // -2.0
boolean fpRelation = dr2  == (-7.0) - (-5.0) * (long)(-7.0 / -5.0);  // true
float   fr3 = -7.0F %  0.0F;   // NaN

Additive Binary Operators: +, -

The addition operator + and the subtraction operator - behave as their names imply: add or subtract values. The binary operator + also acts as string concatenation if any of the operands is a string (see Section 3.6, p. 62).

Additive operators have lower precedence than all the other arithmetic operators. Table 3.4 includes examples that show how precedence and associativity are used in arithmetic expression evaluation.

Table 3.4. Examples of Arithmetic Expression Evaluation

Arithmetic Expression

Evaluation

Result When Printed

3 + 2 - 1

((3 + 2) - 1)

4

2 + 6 * 7

(2 + (6 * 7))

44

-5+7- -6

(((-5)+7)-(-6))

8

2+4/5

(2+(4/5))

2

13 % 5

(13 % 5)

3

11.5 % 2.5

(11.5 % 2.5)

1.5

10 / 0

ArithmeticException

 

2+4.0/5

(2.0+(4.0/5.0))

2.8

4.0 / 0.0

(4.0 / 0.0)

Infinity

-4.0 / 0.0

((-4.0) / 0.0)

-Infinity

0.0 / 0.0

(0.0 / 0.0)

NaN

Numeric Promotions in Arithmetic Expressions

Unary numeric promotion is applied to the single operand of unary arithmetic operators - and +. In other words, when a unary operator is applied to an operand of byte, short or char type, the operand is first promoted to a value of type int, with the evaluation resulting in an int value. If the conditions for implicit narrowing conversion are not fulfilled (p. 48), assigning the int result to a variable of these types will require an explicit cast. This is demonstrated by the following example, where the byte operand b is promoted to an int in the expression (-b):

byte b = 3;           // int literal in range. Implicit narrowing.
b = (byte) -b;        // Explicit narrowing on assignment required.

Binary numeric promotion is applied to operands of binary arithmetic operators. Its application leads to automatic type promotion for the operands. The result is of the promoted type, which is always type int or wider. For the expression at (1) in Example 3.1, numeric promotions proceed as shown in Figure 3.3. Note the integer division performed in evaluating the subexpression (c / s).

Example 3.1 Numeric Promotion in Arithmetic Expressions
public class NumPromotion {
    public static void main(String[] args) {
        byte   b = 32;
        char   c = 'z';  // Unicode value 122 (\u007a)
        short  s = 256;
        int    i = 10000;
        float  f = 3.5F;
        double d = 0.5;
        double v = (d * i) + (f * - b) - (c / s);     // (1) 4888.0D
        System.out.println("Value of v: " + v);
    }
}

Output from the program:

Value of v: 4888.0
Figure 3.3. Numeric Promotion in Arithmetic Expressions

graphics/03fig03.gif

In addition to the binary numeric promotions in arithmetic expression evaluation, the resulting value can undergo an implicit widening conversion if assigned to a variable.

byte   b = 10;
short  s = 20;
char   c = 'z';      // 122 (\u007a) 
int    i = s * b;    // Values in s and b converted to int.
long   n = 20L + s;  // Value in s converted to long.
float  r = s + c;    // Values in s and c promoted to int, followed by implicit
                     // widening conversion of int to float on assignment.
double d = r + i;    // value in i promoted to float, followed by implicit
                     // widening conversion of float to double on assignment.

Binary numeric promotion for operands of binary operators implies that each byte, short, or char operand of a binary operator is promoted to type int or broader numeric type, if necessary. As with unary operators, care must be exercised in assigning the value resulting from applying a binary operator to operands of these types.

short h = 40;          // OK: int converted to short. Implicit narrowing.
h = h + 2;             // Error: cannot assign an int to short.

The value of expression h + 2 is of type int. Although the result of the expression is in the range of short, this cannot be determined at compile time. The assignment requires an explicit cast.

h = (short) (h + 2);   // OK

Notice that applying the cast to the int value 2 only does not work:

h = h + (short) 2;     // Requires an additional cast.

In this case, binary numeric promotion leads to an int value as the result of evaluating the expression on the right-hand side and, therefore, requires an additional cast to narrow it to a short value.

Arithmetic Compound Assignment Operators: *=, /=, %=, +=, -=

A compound assignment operator has the following syntax:


<variable> <op>= <expression>

and the following semantics:


<variable> = (<type>) (<variable> <op> (<expression>))

The type of the <variable> is <type>, and the <variable> is evaluated only once. Note the cast and the parentheses implied in the semantics. Here <op>= can be any of the compound assignment operators specified in Table 3.1. The compound assignment operators have the lowest precedence of all the operators in Java, allowing the expression on the right-hand side to be evaluated before the assignment. Table 3.5 defines the arithmetic compound assignment operators.

Table 3.5. Arithmetic Compound Assignment Operators

Expression:

Given T as the Numeric Type of x, the Expression Is Evaluated as:

x *= a

x = (T) ((x) * (a))

x /= a

x = (T) ((x) / (a))

x %= a

x = (T) ((x) % (a))

x += a

x = (T) ((x) + (a))

x -= a

x = (T) ((x) - (a))

The implied cast, (T), in the compound assignments becomes necessary when the result must be narrowed to the destination type. This is illustrated by the following examples:

int i = 2;
i *= i + 4;             // (1) Evaluated as i = (int) (i * (i + 4)).

byte b = 2;
b += 10;                // (2) Evaluated as b = (byte) ((int) b + 10).
b = b + 10;             // (3) Will not compile. Explicit cast required.

At (1) the source int value is assigned to the destination int variable, and the cast in this case is an identity conversion (i.e., conversion from a type to the same type). Such casts are permitted. However, at (2), the source value is an int value because the byte value in b is promoted to int to carry out the addition, and to assign it to a destination byte variable requires an implicit narrowing conversion. The situation at (3) with simple assignment will not compile, because implicit narrowing is not applicable.

The <variable> is only evaluated once in the expression, not twice, as one might infer from the definition of the compound assignment operator. In the following assignment, a[i] is only evaluated once:

a[i] += 1;

Implicit narrowing conversions are also applied for increment and decrement operators (see Section 3.7, p. 63).

Other compound assignment operators include boolean logical (see Section 3.11, p. 71), bitwise (see Section 3.13, p. 78), and shift (see Section 3.14, p. 83) operators.