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 expression
s 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:
"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]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
And, here's C:
Interestingly, when I did this with the expressions#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
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 int
s, another for
float
s, and another for String
s.
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
instead of the simplerif (test) {return true;} else {return false;}
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:
and the same program, compiled by gcc:#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
<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