The Department of Computer Science & Engineering
|
|
STUART C. SHAPIRO: CSE
305
|
w
- x / y - z or w / x - y / z. Operators with
higher precedence are done first. Parentheses are used to override
precedence. Languages tend to be similar in their operator precedence
order.
x - y - z or x / y / z.
Most operators in most languages are left associative, except that
exponentiation (when included) is usually right associative.
Parentheses are, again, used to override associativity.
A mathematical operator, , is associative when ((x y) z) = (x (y z)), so that the order of evaluation does not matter. Addition and multiplication are mathematically associative, but may not be associative computationally. For example,
In particular, when adding a series of floating point numbers, it is better to add them starting with the smallest terms than starting with the largest terms.bsh % print(4.+4.+4.+4.+4.+4.+4.+4.+4.+4.+4.+3e17); 3.0000000000000006E17 bsh % print(3e17+4.+4.+4.+4.+4.+4.+4.+4.+4.+4.+4.); 3.0E17
<expression>1
<expression>2, is
<expression>1 or
<expression>2 evaluated first? It will not
matter unless one of the expressions has a side effect.
A side effect occurs when the evaluation of an expression causes a change to some variable, rather than only producing a value. Here's an example in Java:
public class OperandOrder {
public static int x;
public static int f(int y) {
x++;
return y;
}
public static void main (String[] args) {
int result;
x = 3;
result = x + f(5);
System.out.println("First evaluation = " + result);
x = 3;
result = f(5) + x;
System.out.println("Second evaluation = " + result);
} // end of main ()
}// OperandOrder
------------------------------------------------
<cirrus:Programs:1:102> javac OperandOrder.java
<cirrus:Programs:1:103> java OperandOrder
First evaluation = 8
Second evaluation = 9
"The Java programming language guarantees that the operands of
operators appear to be evaluated in a specific evaluation order,
namely, from left to right." [Java
Language Specification Second Edition, Section 15.7]
And, here's C:
#include <stdio.h>
int x;
int f(y)
int y; {
x++;
return y;
}
int g(y)
int y; {
return y;
}
int main() {
int result;
x = 3;
result = g(x) + f(5);
printf("First evaluation = %3d\n", result);
x = 3;
result = f(5) + g(x);
printf("Second evaluation = %3d\n", result);
return 0;
}
------------------------------------------------
<cirrus:Programs:1:276> operandOrder.out
First evaluation = 8
Second evaluation = 9
Interestingly, when I did this with the expressions x +
f(5) and f(5) + x, the result was 9
both times.
"Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified." [ISO C Standard, Sect. 6.5[#3]]
Optimizing compilers may change the order of operand evaluation. They sometimes allow the programmer to specify that a certain section of code should not be optimized.
+
compiling into one prodecure for ints, another for
floats, and another for Strings.
C++ and some other languages allow the programmer to supply additional overloadings to most or all of its operators.
int to double.
A narrowing conversion converts a value from type A to type B where
multiple values of type A convert to the same value of type B (even
within reasonable bounds of range and precision). An example is
conversion from double to int.
However, here's an example where even widening doesn't work because of loss of precision:bsh % int i; bsh % double x; bsh % i = 3; bsh % x = i; bsh % print(x); 3.0 bsh % x = 3.5; bsh % i = (int)x; bsh % print(i); 3
Usually, if the operands of one operator are of different types, the value of one is automatically converted (coerced) to the type of the other as long as that is a widening conversion, but a narrowing conversion must be explicitly specified by a cast. Ada performs very few coercions, requiring explicit casts in most cases.bsh % float y; bsh % y = 123456792; bsh % print(y); 1.23456792E8 bsh % y = 123456793; bsh % print(y); 1.23456792E8
x == y. Note, however, that two
floating-point values, computed differently, are seldom equal.
Boolean expressions apply a Boolean operator to Boolean values. An
example is i < max && a[i] == x. Usually, Boolean
operators have lower precedence than relational and arithmetic
operators. However, using parentheses is often a good idea, for
example, (i < max) && (a[i] == x).
A standard warning given to novice programmers is that i <
j < k is not correct;
it must be written (i <
j) && (j < k). Since C uses 0 and
1 for Boolean values, one can get:
#include <stdio.h>
int main() {
int i=5, j=7, k=3;
if (i < j < k) {
printf("It's true that %d < %d < %d\n", i, j, k);
}
return 0;
}
-----------------------------------------------------
<cirrus:Programs:1:109> gcc -Wall boolTest.c -o boolTest.out
<cirrus:Programs:1:110> boolTest.out
It's true that 5 < 7 < 3
Trust the Boolean type! I have often seen code like
if (test) {return true;}
else {return false;}
instead of the simpler
return test;
flag ? count1 : count2 = 0; is
legal in C++, Java, and C#, but neither our Java compiler nor our
BeanShell allowed it.
x = y; means the same
as x = x y; for most operators,
.
i = j = k assigns the value of
k to j and then to i.
In Perl the value of the assignment operation is the l-value of the left-hand side, even though assignment is still right-associative. The l-value is dereferenced to its r-value if the assignment expression is on a right-hand side, but if it's on a left-hand side, the l-value may be stored into. Here's an example, based on L. Wall, T. Christiansen & J. Orwant, Programming Perl, (Sebastopol, CA: O'Reilly) p. 25.
#! /util/bin/perl $x = $y = 3; $temp = 100; ($temp *= 9/5) += 32; print "x = $x, y = $y, temp = $temp\n"; ----------------------------------------- <cirrus:Programs:1:123> perl assignmnt.perl x = 3, y = 3, temp = 212
++ and
--. The tricky issue is the postfix versions. In Java,
it is clear that the semantics of x++ in any context is
x fetch x x fetch 1 + store pop. Here's an example:
public class UnaryAsngmnt {
public static void main (String[] args) {
int x=3, y = 0;
System.out.println("x = " + x);
System.out.println("y = " + y);
y = x + x++;
System.out.println();
System.out.println("y = x + x++;");
System.out.println("x = " + x);
System.out.println("y = " + y);
x = x++;
System.out.println();
System.out.println("x = x++;");
System.out.println("x = " + x);
} // end of main ()
}// UnaryAsngmnt
--------------------------------------------------
<cirrus:Programs:1:124> javac UnaryAsngmnt.java
<cirrus:Programs:1:125> java UnaryAsngmnt
x = 3
y = 0
y = x + x++;
x = 4
y = 6
x = x++;
x = 4
In C the tricky uses of these operators are officially undefined since the order of evaluation of operands is unspecified. Let's see this in two C compilers. In cc:
#include <stdio.h>
int main() {
int x=3, y=0;
printf("x = %d, y = %d\n", x, y);
y = x + x++;
printf("\ny = x + x++;\n");
printf("x = %d, y = %d\n", x, y);
x = x++;
printf("\nx = x++;\n");
printf("x = %d, y = %d\n", x, y);
x = x++ + x;
printf("\nx = x++ + x;\n");
printf("x = %d, y = %d\n", x, y);
x = x++ + x++;
printf("\nx = x++ + x++;\n");
printf("x = %d, y = %d\n", x, y);
return 0;
}
---------------------------------------------
<cirrus:Programs:1:138> cc unaryAsngmnt.c -o unaryAsngmnt.out
<cirrus:Programs:1:139> unaryAsngmnt.out
x = 3, y = 0
y = x + x++;
x = 4, y = 6
x = x++;
x = 5, y = 6
x = x++ + x;
x = 11, y = 6
x = x++ + x++;
x = 24, y = 6
and the same program, compiled by gcc:
<cirrus:Programs:1:140> gcc -Wall unaryAsngmnt.c -o unaryAsngmnt.out unaryAsngmnt.c: In function `main': unaryAsngmnt.c:8: warning: operation on `x' may be undefined unaryAsngmnt.c:12: warning: operation on `x' may be undefined unaryAsngmnt.c:16: warning: operation on `x' may be undefined unaryAsngmnt.c:20: warning: operation on `x' may be undefined unaryAsngmnt.c:20: warning: operation on `x' may be undefined <cirrus:Programs:1:141> unaryAsngmnt.out x = 3, y = 0 y = x + x++; x = 4, y = 6 x = x++; x = 5, y = 6 x = x++ + x; x = 6, y = 6 x = x++ + x++; x = 7, y = 6
public class AsngmntOrder {
public static void main (String[] args) {
int i=0, a[] = {5, 5, 5};
System.out.println("i = " + i);
System.out.println("a = [" + a[0]
+ ", " + a[1]
+ ", " + a[2] + "]");
a[i] = i++;
System.out.println();
System.out.println("a[i] = i++;");
System.out.println("i = " + i);
System.out.println("a = [" + a[0]
+ ", " + a[1]
+ ", " + a[2] + "]");
a[i++] = i;
System.out.println();
System.out.println("a[i++] = i;");
System.out.println("i = " + i);
System.out.println("a = [" + a[0]
+ ", " + a[1]
+ ", " + a[2] + "]");
} // end of main ()
}// AsngmntOrder
------------------------------------------------
<cirrus:Programs:1:129> javac AsngmntOrder.java
<cirrus:Programs:1:130> java AsngmntOrder
i = 0
a = [5, 5, 5]
a[i] = i++;
i = 1
a = [0, 5, 5]
a[i++] = i;
i = 2
a = [0, 2, 5]
C leaves this case officially undefined,
This paragraph renders undefined statement expressions
such as
i = ++i + 1;
a[i++] = i; |
while allowing
i = i + 1;
a[i] = i; [ISO CStandard, Sect. 6.5(#58)]
x = y = ... = <exp> lets you
assign the same value to multiple variables, but usually the
assignment of multiple values to multiple variables requires multiple
assignment statements in sequential order.
Common Lisp's assignment "statement" allows multiple values to be stored in multiple variables in sequential order:
There is also a parallel version that allows multiple values to be stored in multiple variables in parallel order. This allows for the swapping of two variables without using an explicit temporary:cl-user(17): (setf x 1 y 2 z 3) 3 cl-user(18): x 1 cl-user(19): y 2 cl-user(20): z 3
cl-user(21): x 1 cl-user(22): y 2 cl-user(23): (psetf x y y x) nil cl-user(24): x 2 cl-user(25): y 1
Perl also allows parallel assignment using its array (list) environment:
#! /util/bin/perl $x = 3; $y = 5; print "First, x = $x, y = $y\n\n"; ($x, $y) = ($y, $x); print "Afterwards, x = $x, y = $y\n"; --------------------------------------- <cirrus:Programs:1:147> perl parallelasgnmnt.perl First, x = 3, y = 5 Afterwards, x = 5, y = 3