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.
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, because 3 is less than 5; for the same reason.
We compare values with the comparison operators. Here are the comparisons that Python recognizes:
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
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!).
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.
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.
# 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.
Python makes an important (but subtle) distinction between the following two questions:
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
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).
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.
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.