| Fortran Language Reference Manual, Volume 1 - S-3692-51 | ||
|---|---|---|
| Prev Section | Chapter 7. Expressions and Assignments | Next Section |
The form of the expression and the meaning of the operations establish the interpretation; once established, a compiler can evaluate the expression in any way that provides the same interpretation with one exception; parentheses specify an order of evaluation that cannot be modified. This applies to both intrinsic operations and defined operations.
Essentially, two sorts of alternative evaluations are allowed:
The rearrangement of an expression that yields an equivalent expression; for example, A + B + C can be evaluated equivalently as A + (B + C) and would improve the efficiency of the compiled program if B + C were a subexpression whose value had already been computed.
The partial evaluation of an expression because the value of the unevaluated part can be proven not to affect the value of the entire expression. For example, when one operand of a disjunction (.OR. operator) is known to be true, the other operand need not be evaluated to determine the result of the operation. To be specific, the operand A * B < C need not be evaluated in the expression A < B .OR. A * B < C if A < B is true. This freedom for a compiler to use alternative equivalent evaluations permits the compiler to produce code that is more optimal in some sense (for example, fewer operations, array operations rather than scalar operations, or a reduction in the use of registers or work space), and thereby produce more efficient executable code.
Before describing in more detail the possible evaluation orders, four basic issues need to be addressed, namely, definition of operands, well-defined operations, functions (and defined operations) with side effects, and equivalent interpretations.
Definition status is described in detail in the Fortran Language Reference Manual, Volume 2. For the purpose of evaluation of expressions, it is required that each operand is defined, including all of its parts, if the operand is an aggregate (an array, a structure, or a string). If the operand is a subobject (part of an array, structure, or string), only the selected part is required to be defined. If the operand is a pointer, it must be associated with a target that is defined. An integer operand must be defined with an integer value rather than a statement label.
For the numeric intrinsic operations, the operands must have values for which the operation is well-defined. For example, the divisor for the division operation must be nonzero, and the result of any of the numeric operations must be within the exponent range for the result data type; otherwise, the program is not standard conforming. Other cases include limitations on the operands of the exponentiation operation **: for example, a zero-valued first operand must not be raised to a nonpositive second operand; and a negative-valued first operand of type real cannot be raised to a real power.
The third issue is functions with side effects. In Fortran, functions are allowed to have side effects; that is, they are allowed to modify the state of the program so that the state is different after the function is invoked than before it is invoked. This possibility potentially affects the equivalence of two schemes for evaluating an expression, particularly if the function modifies objects appearing in other parts of the expression. However, Fortran prohibits the formation of statements with these kinds of side effects. That is, a function (or defined operation) within a statement is not permitted to change any entity in the same statement. Exceptions are those statements that have statements within them, for example, an IF statement or a WHERE statement. In these cases, the evaluation of functions in the logical expressions in parentheses after the IF keyword or WHERE keyword are allowed to affect objects in the statement following the closing right parenthesis. For example, if F and G are functions that change their actual argument I, the following statements are valid, even though I is changed when the functions are evaluated:
IF (F(I)) A = I WHERE (G(I)) B = I |
The following statements are examples of statements that are not valid because F and G change I, which is used elsewhere in the same statement:
A(I) = F(I) Y = G(I) + I |
It is also not valid for there to be two function references in a statement, if each causes a side effect and the order in which the functions are invoked yields a different final status, even though nothing in the statement is changed.
The fourth issue is equivalent interpretation. For the numeric intrinsic operations, the definition of equivalent interpretation is defined as being mathematical equivalence of the expression, not computational equivalence. Mathematical equivalence assumes exact arithmetic (no rounding errors and infinite exponent range) and thus assumes the rules of commutativity, associativity, and distributivity as well as other rules that can be used to determine equivalence (except that the order of operations specified by parentheses must be honored). Under these assumptions, two evaluations are mathematically equivalent if they yield the same values for all possible values of the operands. A + B + C and A + (B + C) are thus mathematically equivalent but are not necessarily computationally equivalent because of possible different rounding errors. On the other hand, I/2 and 0.5 * I (where I is an integer) is a mathematical difference because of the special Fortran definition of integer division.
Table 7-23, gives examples of equivalent evaluations of expressions where A, B, and C are operands of type real or complex, and X, Y, and Z are of any numeric type. All of the variables are assumed to be defined and have values that make all of the operations in this table well-defined.
Table 7-23. Equivalent Evaluations for Numeric Intrinsic Operations
Expression | Equivalent evaluations |
|---|---|
X+Y | Y+X |
X*Y | Y*X |
-X+Y | Y-X |
X+Y+Z | X+(Y+Z) |
X-Y+Z | X-(Y-Z) |
X*A/Z | X*(A/Z) |
X*Y-X*Z | X*(Y-Z) |
A/B/C | A/(B*C) |
A/5.0 | 0.2*A |
Table 7-24 provides examples of alternative evaluations that are not valid and are not mathematically equivalent to the original expression. In addition to the operands of the same names used in Table 7-23, Table 7-24 uses I and J as operands of type integer. Recall that when both operands of the division operator are of type integer, a Fortran integer division truncates the result toward zero to obtain the nearest integer quotient.
For character, relational, and logical intrinsic operations, the definition of the equivalence of two evaluations is that, given the same values for their operands, each evaluation produces the same result. The definition for equivalence of two evaluations of the same defined operation also requires the results to be the same; note that this definition is more restrictive than for the numeric intrinsic operations, because only mathematical equivalence need be preserved for numeric operations. As described for numeric intrinsic operations, a compiler can choose any evaluation scheme equivalent to that provided by the interpretation. Table 7-25 gives some equivalent schemes for evaluating a few example expressions. For these examples, I and J are of type integer; L1, L2, and L3 are of type logical; and C1, C2, and C3 are of type character of the same length. All of the variables are assumed to be defined.
Table 7-25. Equivalent Evaluations Of Other Expressions
Expression | Equivalent evaluations |
|---|---|
I .GT. J | (I-J) .GT. 0 |
L1 .OR. L2 .OR. L3 | L1 .OR. (L2 .OR. L3) |
L1 .AND. L1 | L1 |
C3 = C1//C2 | C3=C1 (C1, C2, C3 all of the same length) |
These rules for equivalent evaluation schemes allow the compiler to not evaluate any part of an expression that has no effect on the resulting value of the expression. Consider the expression X*F(Y), where F is a function and X has the value 0. The result will be the same regardless of the value of F(Y); therefore, F(Y) need not be evaluated. This shortened evaluation is allowed in all cases, even if F(Y) has side effects. In this case every data object that F could affect is considered to be undefined after the expression is evaluated (that is, it does not have a predictable value).
The appearance of an array element, an array section, or a character substring reference requires, in most cases, the evaluation of the expressions that are the subscripts, strides, or substring ranges. The type or type parameters of the containing expression are not affected by the evaluation of such subscript, stride, or substring range expressions. It is not necessary for these expressions to be evaluated, if the array section can be shown to be zero-sized or the substring can be shown to be of a zero-length by other means. For example, in the expression A(1:0) + B( expr1: expr2), expr1 and expr2 need not be evaluated because the conformance rules for intrinsic operations require that the section of B be zero-sized.
The type and type parameters, if any, of a constructor are not affected by the evaluation of any expressions within the constructor.
Parentheses within the expression must be honored. This is particularly important for computations involving numeric values in which rounding errors or range errors may occur or for computations involving functions with side effects.
| Prev Section | Table of Contents | Title Page | Index | Next Section |
| Interpretation of Expressions | Up one level | Assignment |