Making Decisions : The Comparison Operators

Comparisons are the heart of decision-making. Decision-making is the heart of controlling what our programs do. We’ll start off looking at the basic comparison operators in Greater Than? Less Than?. We’ll look at some more advanced comparison techniques in More Sophisticated Comparisons.

We’ll take second look at how the logic operators of and and or work in Taking Other Short-Cuts.

We’ll answer some additional questions in Comparison FAQ.

Greater Than? Less Than?

Ordinary arithmetic operators are functions that map some numbers to another number. For example, addition maps two numbers to the number which is their sum: 3+5 maps to 8. Similarly, multiplication maps two numbers to their product, :we could say math:3 times 5 mapsto 15.

A comparison maps two numbers to a boolean value that reflects the relationship between the numbers. For example, 3 < 5 \mapsto \text{True} because 3 is less than 5; 3 \ge 5 \mapsto \text{False} for the same reason.

We compare values with the comparison operators. Here are the comparisons that Python recognizes:

  • Less than (<) is <.
  • Greater than (>) is >.
  • Less than or equal to (\leq) is <=.
  • Greater than or equal to (\geq) is >=.
  • Equal to (=) is ==.
  • Not equal to (\neq) is !=.

Why is == used to test for equality? The problem is that mathematicians use the symbol = pretty freely, but we have to provide more formal definitions. Python uses = for the assignment statement (The Assignment Statement). To distinguish between assignment and comparison, Python uses == to mean comparison.

Here are some examples. You can see that a comparison produces a boolean result of either True or False.

>>> 10 > 2
True
>>> 10 >= 9+1
True
>>> 10 >= 9+2
False
>>> 1 == 2
False
>>> 1 != 2
True

The = and == Problem. Here’s a common mistake. We’ve used a single = (assignment), when we meant to use == (comparison). We get a syntax error because we have a literal 99 on the left side of the = statement.

>>> 99 = 2
  File "<stdin>", line 1
SyntaxError: can't assign to literal

An Unexpected Coercion. Here’s a strange thing that can happen because of the way Python converts between numeric type data and boolean type data. First, look at the example, then try to figure out what happened.

>>> (10 >= 9) + 2
3

Because of the (), the 10 >= 9 is evaluated first. What is the result of that comparison?

How can it make sense to compute a sum of a boolean value (True or False) and a number? It doesn’t, really, but Python tries anyway. It must have converted the boolean result of 10 >= 9 to a number. Try the following to see what has happened.

>>> (10 >= 9) + 2
3
>>> 10 >= 9
True
>>> int( 10 >= 9 )
1
>>> int( 10 >= 9 ) + 2
3

Tip

Debugging Comparison Operators

The most common problem people have with the comparison operators is to attempt to compare things which cannot meaningfully be compared. They ask, in essence, “which is larger, the Empire State Building or the color green?” An expression like 123 < 'a' doesn’t really make a lot of sense, even though it is legal Python.

Python copes with senseless comparisons using it’s coercion rules. In this case, the number 123 is coerced to a string '123', and the two strings are compared using the ordinary alphabetical order rules. In this case, digits come before letters and any number will be less than any word.

Sorting out the rules for coercion and comparison can be very confusing. Consequently, it should be avoided by exercising reasonable care in writing sensible programs. You should inspect your program to be sure you are comparing things sensibly. You can also put in explicit conversions using the various factory functions in Functions are Factories (really!).

More Sophisticated Comparisons

Comparisons can be combined in Python, unlike most other programming languages. For example, we can ask: 0 <= a < 6 which has the usual mathematical meaning. We’re not forced to write out the longer form: 0 <= a and a < 6.

Here’s an example of checking for the “middle twelve” in Roulette. Since the number is random, your results may vary.

>>> import random
>>> spin= random.randrange(38)
>>> 13 <= spin <= 24
False
>>> spin
12

Writing 13 <= somethingComplex <= 24 instead of 13 <= somethingComplex and somethingComplex <= 24 is particularly useful when somethingComplex is actually some complex expression that we’d rather not repeat.

Proper Floating-Point Comparison. Exact equality between floating-point numbers is a dangerous concept. During a lengthy computation, round-off errors and conversion errors in floating-point numbers will accrue a tiny error term. In Better Arithmetic Through Functions, we saw answers that were off in the 15th decimal place. These answers are close enough to be equal for all practical purposes, but one or more of the 64 bits may not be identical.

The following technique is the appropriate way to do the comparison between floating-point numbers a and b.

abs(a-b)/a<0.0001

Rather than ask if the two floating-point values are the same, we ask if they’re close enough to be considered the same. For example, run the following tiny program.

floatequal.py

# Are two floating-point values really completely equal?
from __future__ import print_function
a,b = 1/3.0, .1/.3
print(a, b, a==b)
diff= abs(a-b)/a
print(diff, diff < 0.0001)

When we run this program, we get the following output

$ python floatequal.py
0.333333333333 0.333333333333 False
1.66533453694e-16 True

The two values appear the same when printed. Yet, on most platforms, the == test returns False. They are not identical values. They differ at the 16th digit past the decimal point.

This is a consequence of representing real numbers with only a finite amount of binary precision. Certain repeating decimals get truncated, and these truncation errors accumulate in our calculations.

There are ways to avoid this problem; one part of this avoidance is to do the algebra necessary to postpone doing division operations. Division introduces the largest number erroneous bits onto the trailing edge of our numbers. The most important part of avoiding the problem is never to compare floating-point numbers for exact equality.

The Same Thing or The Same Value? The is Comparison

Python makes an important (but subtle) distinction between the following two questions:

  • Do two objects have the same value?
  • Are two objects references to the same thing?

Consider the following

a = 123456789
b = a

The variable a refers to 123456789. The variable b also refers to the same object.

When we evaluate the following, the results aren’t surprising.

>>> a=123456789
>>> b=a
>>> a is b
True
>>> a == b
True

The is operator tells us that variable a and variable b are two different labels attached to the same underlying object.

The == operator tells us that variable a and variable b refer to objects which have the same numeric value. In this case, since a is b is True, it’s not surprising that a == b.

Not the Same Thing. In most cases, however, we’ll have situations like the following. We’ll create two distinct objects that have the same numeric value.

>>> a=123456789
>>> c=a*1
>>> c is a
False
>>> c == a
True
>>> c is not a
True

In this example. we’ve evaluated a simple operator (*), which created a new object. We know it’s a new object because c is a is False (also, c is not a is True). However, this new object has the same numeric value as a.

Common Use. The most common use for is and is not is when comparing specific objects, not generic numeric values. We do this mostly with the object None.

>>> variable = None
>>> variable
>>> variable is None
True
>>> anotherVariable = 355/113.0
>>> anotherVariable is None
False
>>> anotherVariable
3.1415929203539825

Comparison Exercises

  1. Come Out Win.

    Assume d1 and d2 have the numbers on two dice. Assume this is the come out roll in Craps. Write the expression for winning (7 or 11). Write the expression for losing (2, 3 or 12). Write the expression for a point (4, 5, 6, 8, 9 or 10).

  2. Field Win.

    Assume d1 and d2 have the numbers on 2 dice. The field pays on 2, 3, 4, 9, 10, 11 or 12. Actually there are two conditions: 2 and 12 pay at one set of odds (2:1) and the other 5 numbers pay at even money. Write two conditions under which the field pays.

Comparison FAQ

Why do the logical operators short-circuit? Isn’t that an egregious violation of the eval-apply cycle?

In Execution – Two Points of View we emphasized the evaluate-apply cycle like it was a natural law or divine writ, direct from the almighty. Yet, here we’re saying that the and and or operators violate this law.

First, we’re only bending the law. The essential principle is still followed, we’re just extending the rule a little: all of the logically necessary parts of an expression are evaluated first.

The alternatives to the short-circuit sense of and and or are either much more complex logic operators (and, or, cand and cor) or decomposing relatively simple logic into a complex sequence of statements, using a if statement instead of a short-circuit logic operator.

The objective of software is to capture knowledge of processing in a clear and formal language. Fussy consistency in this case doesn’t help achieve clarity.

Table Of Contents

Previous topic

Truth and Logic : Boolean Data and Operators

Next topic

Advanced Logic Operators

This Page