CSCI A201/A597

Lecture Notes Twelve

Second Summer 2000


Decisions
The programs we have seen so far are fairly inflexible. Except for variations in the input they work the same way with every program run.

One of the essential features of nontrivial computer programs is the ability to make decisions... ... and to carry out different actions, depending on the nature of the inputs.

The goal of this chapter is to learn how to program simple and complex decisions. Learning that will greatly increase our expressive power in Java.

In some of the previous assignments we went to great length to either fake decisions by building them into clever formulas. Let's implement the withdraw method so that you cannot withdraw more money that you have in your account.

That is, the withdraw method must make a decision: whether to allow the withdrawal or not. The if statement is used to implement a decision. It has two parts: a test and a body.

If the test succeeds, the body of the if statement is executed. Here's an example, as a flowchart:
And in Java:
if (amount <= balance)
  balance = balance - amount;
The test is in red and the body of the if statement is in blue.

A statement such as
balance -= amount; 
is called a simple statement.
A conditional statement, such as:
if (x > 0) y = x; 
is called a compound statement.

Our programs remain sequences of statements, we just allow compound statements such as the if statements in our programs. Soon we will encounter, and start using, other compound statements: the so-called loops.

To implement a real alternative, use the if/else statement: ... if the condition is satisfied then the first block is executed; otherwise the second is executed.
In Java, and with colors, that is:
if (amount <= balance)
  balance = balance - amount; 
else
  balance = balance - OVERDRAFT_PENALTY;

Quite often the body of an if statement consists of multiple statements that must be executed in sequence whenever the test is succesful. These statements must be grouped together to form a block statement by enclosing them in braces { }.

Here's a somewhat contrived example:
if (x < y) {
  temp = x; 
  x = y; 
  y = temp; 
} 
We assume, of course, that x, y, and temp have been declared and that (at least as far as x and y are concerned) they have been initialized already.

Can you briefly say what the code is doing? It makes sure that of the two values the larger one is always in x.

Very good. What were we saying about braces? They group statements together.

What if we drop them? Then the code no longer works as intended.

So what is the syntax of an if statement? The body of an if statement (or an else alternative) must be a statement (just one).

But it can be:
  • a simple statement
  • a compound statement (such as another if statement), or
  • a block statement
It's good to get into the habit of using braces (and thus block statements) all the time.

Yes, as we will see when we get to exercises, shortly. I can hardly wait. But first, let's analyze the if statement closer, and look at what makes a test.

Its outcome is either true or false. In many cases the test compares two values.

Comparison operators such as <= are called relational operators. Java has six relational operators.
Java Math Description
> > Greater thaqn
>= Greater than or equal
< < Less than
<= Less than or equal
== = Equal
!= Not equal

The == operator is initially confusing to most newcomers to Java. In Java, the = symbol already has a meaning, namely assignment.

The == denotes equality testing:
a = 5; // assign 5 to a 
if (a == 3) // tests whether a equals 3 
  System.out.println("a is equal to 3");
else 
  System.out.println("a is not equal to 3"); 
You will have to remember to use == for equality testing and to use = for assignment.

Floating point numbers have only a limited precision, and calculations can introduce roundoff errors. That means we need to be careful when we want to test if two floating point quantities are representing the same thing.

Here's an example:
double r = Math.sqrt(2); 
if (r * r == 2) 
  System.out.println(r * r + " == 2"); 
else 
  System.out.println(r * r + " != 2"); 
Unfortunately such roundoff errors are unavoidable.

In most circumstances it does not make a lot of sense to compare floating point numbers exactly. Instead we should test whether they're close enough.

That is, the absolute value of their difference should be less than some threshold. Mathematically, x and y are close enough if

... for a very small number, . Greek letter epsilon is commonly used to denote a very small quantity.

It is common to set to 10-14 when comparing double numbers. However, this is not always good enough.

Indeed, if the two numbers are very big, then one can be a roundoff of the other even if their difference is much bigger than 10-14. To overcome this problem we need to normalize: we divide by the magnitude of the numbers before comparing how close they are.

So x and y are close enough if
And to avoid division by zero it is better to test whether

In Java, this is:
Math.abs(x - y) <= EPSILON * Math.max(Math.abs(x), Math.abs(y))
OK, I think I understand how I test numbers for equality.

What else can we test for equality? How about Strings?

To test whether two strings are equal to each other, ... ..., that is, that their contents is the same, ...

... one must use method equals. Why not use == like for numbers?

Strings are objects. And so are Rectangles.

If you compare two object references with the == operator, ... ... you test whether the references refer to the same object.

That's because you check to see whether the two locations contain the same thing. Which is in each case an address, to an actual object.

Let's see some examples.
Rectangle a = new Rectangle(5, 10, 20, 30); 
Rectangle b = a;
Rectangle c = new Rectangle(5, 10, 20, 30); 
The comparison a == b is true.

Both object variables refer to the same object. But the comparison a == c is false.

The two object variables refer to different objects.
It does not matter that the objects have identical contents.

You can use the equals method to test whether two rectangle have the same contents. Thus a.equals(c) is true.

And so is c.equals(b) obviously. Same with Strings, so we will have to remember to use equals for string comparison.

In Java letter case matters. Thus
"harry".equals("HARRY")
evaluates to false.
But
"harry".equalsIgnoreCase("HARRY")
evaluates to true.

Even if two strings don't have "identical" contents we may still want to know the relationship between them. The compareTo method compares strings in dictionary order.

If string1.comparesTo(string2) < 0 ... then string1 comes before string1 in dictionary order.

If string1.comparesTo(string2) > 0 ... then string1 comes after string2 in dictionary order.

If string1.comparesTo(string2) == 0 ... then the two strings have identical contents.

You should look this method up in class String. Actually the dictionary ordering used by Java ius slightly different from that of a normal dictionary.

Java is case-sensitive and sorts characters by listing numbers first, then uppercase characters, then lowercase characters. For example 1 comes before B which comes before a.

And the space character comes before all other characters. Can we describe the comparison process a little bit in greater detail?

When comparing two strings, corresponding letters are compared until one of the strings ends or the first difference is encountered. If one of the strings ends, the longer string is considered the later one.

If a character mismatch is found, compare the characters to determine which string comes later in the dictionary sequence. The process is called lexicographic comparison.

That's why "car" comes before "cargo", And "cathode" comes after "cargo" in lexicographic ordering.

Time for a break. I sure think so.

And some exercises too. Yes, but the break first, please.


Last updated: July 9, 2000 by Adrian German for A201