We'll look at the basic comparison operators. We'll also look at the
partial evaluation rules of the logic operators to show how we can build
more useful expressions. Finally, we'll look at floating-point equality
tests, which are sometimes done incorrectly.
We compare values with the comparison operators. These correspond
to the mathematical functions of <, ≤, >, ≥, = and ≠. Conditional
expressions are often built using the Python comparison operators: <,
<=, >, >=, == and != for less than, less than or equal to,
greater than, greater than or equal to, equal to and not equal
to.
>>>
p1=22./7.
>>>
p2=355/113.
>>>
p1
3.1428571428571428
>>>
p2
3.1415929203539825
>>>
p1 < p2
False
>>>
p1 >= p2
True
When applying a comparison operator, we see a number of
steps.
-
Evaluate both argument values.
-
Apply the comparison to create a boolean result.
-
Convert both parameters to the same type. Numbers are
converted to progressively longer types: plain integer to long
integer to float to complex.
-
Do the comparison.
-
Return True or False.
We call out these three steps explicitly because there are some
subtleties in comparison among unlike types of data; we'll come to this
later when we cover sequences, mappings and classes in Part II, “Data Structures”. Generally, it doesn't make sense to compare unlike
types of data. After all, you can't ask "Which is larger, the Empire
State Building or the color green?"
Comparisons can be combined in Python, unlike most other
programming languages. We can ask: 0 <= a < 6
which
has the usual mathematical meaning. We're not forced to use the longer
form: 0 <= a and a < 6
.
>>>
3 < p1 < 3.2
True
>>>
3 < p1 and p1 < 3.2
True
This is useful when a
is actually some complex
expression that we'd rather not repeat.
Also note that the preceding example had a mixture of integers and
floating-point numbers. The integers were coerced to floating-point in
order to evaluate the expressions.
We can combine the logic operators, comparisons and math. This
allows us to use comparisons and logic to prevent common mathematical
blunders like attempting to divide by zero, or attempting to take the
square root of a negative number.
For example, let's start with this program that will figure the
average of 95, 125 and 132.
sum = 95 + 125 + 132
count = 3
average = float(sum)/count
print average
Initially, we set the variables sum
and
count
. Then we compute the average
using sum
and count
.
Assume that the statement that computes the average
(average=...
), is part of a long and complex program.
Sometimes that long program will try to compute the average of no
numbers at all. This has the same effect as the following short
example.
sum, count = 0, 0
average = float(sum)/count
print average
In the rare case that we have no numbers to average we don't want
to crash when we foolishly attempt to divide by zero. We'd prefer to
have some more graceful behavior.
Recall from the section called “Truth and Logic” that the
and
operator doesn't evaluate the right-hand side
unless the left-hand side is True
. Stated the other
way, the
and
operator
only
evaluates the right side if the left side is True
. We
can guard the division like this:
average = count != 0 and sum/count
print average
This is an example that can simplify certain kinds of complex
processing. If the count is non-zero, the left side is true and the
right side must be checked. If the count is zero, the left side is
False
, the result of the complete
and
operation is False
.
This is a consequence of the meaning of the word
and
. The expression
a and b
means that a
is true as well as b
is true. If a
is false, the value of
b
doesn't really matter, since the whole expression
is clearly false. A similar analysis holds for the word
or
. The expression
a or b
means that one of the two is true; it also means that neither of the two
is false. If a
is true, then the value of
b
doesn't change the truth of the whole
expression.
The statement “It's cold and rainy” is completely
false when it is warm; rain doesn't matter to falsifying the whole
statement. Similarly, “I'm stopping for coffee or a
newspaper” is true if I've stopped for coffee, irrespective of
whether or not I stop for a newspaper.
Floating-Point Comparisons
Exact equality between floating-point numbers is a dangerous
concept. After a lengthy computation, round-off errors in floating point
numbers may have infinitesimally small differences. The answers are
close enough to equal for all practical purposes, but every single one
of the 64 bits may not be identical.
The following technique is the appropriate way to do floating
point comparisons.
abs(a-b)<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.
Example 7.1. floatequal.py
#!/usr/bin/env python
# Are two floating point values really completely equal?
a,b = 1/3.0, .1/.3
print a,b,a==b
print abs(a-b)<0.00001
When we run this program, we get the following output
$
python floatequal.py
0.333333333333 0.333333333333 False
True
$
The two values appear the same when printed. Yet, on most
platforms, the
==
test returns
False
. They are not precisely the same. 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 other part of avoiding the problem is never to
compare floating point numbers for exact equality.