Introducing the built-in operators
Before looking at the various kinds of numbers available, we'll introduce the Python operators. The operators fall into three broad groups:
The differences between these groups are partly subjective. There's only a small technical difference in the way the comparison operators work. Most of the operators are binary, only one (~
) is unary, and a few (+
, -
, *
, **
) can be used in either context.
The +
, -
, *
, /
, and %
operators have meanings similar to those used other programming languages. There is an arithmetic meaning for –
and +
. Python adds the **
operator when raising a number to a power. The **
operator takes higher precedence than the unary form -
; this means that -2**4
is -16
.
Bit-oriented operators apply only to integers. They also apply to sets. These are emphatically not logical operators. The actual logical operators are described in Chapter 5, Logic, Comparisons, and Conditions.
Making comparisons
The comparison operators (<
, >
, ==
, !=
, <=
, >=
) have meanings similar to those used in other programming languages. The coercion rules apply to comparisons between numbers. If the objects are of mixed types, one of them will be coerced "up" the numeric tower from integer to float, or float to complex. The result of a comparison is a Boolean (True
or False
) irrespective of the types of the two operands.
The various coercion rules do not apply to strings or other objects. Strings are not implicitly converted to numbers. 2 != '2'
is true because the integer 2
is not a string '2'
.
Some popular languages (for example, Java, C++) have primitive types such as int
or long
which are not proper objects—they're not instances of a class—and the rules that apply to objects do not apply to them. Java allows the ==
comparison for int
objects, but using the same comparison operator with string objects doesn't compare the characters of the two strings, it only compares the references. This is emphatically not the case with Python. All Python objects are proper instances of a class: the ==
comparison in Python strings compares the two strings character by character.
We'll look at comparisons in more detail in Chapter 5, Logic, Comparisons, and Conditions.
Using integers
Python integers are objects of the class int
. These objects have the largest number of operators, including all of the arithmetic, bit-oriented, and comparison operators.
Integer values are limited by available memory. This means they can be quite large. We can easily compute 1,000!, a number with over 2,500 digits. We'll save the details for Chapter 8, More Advanced Functions. A number of similarly gargantuan size is:
>>> 2**8530 610749...581824
This is a very large number. We've elided most of it. It's easily represented in Python.
Generally, we provide integer literals in decimal, base 10. We can also write literals in three other bases: hexadecimal, octal, and binary.
The prefix of 0x
is the prefix for base 16 values: 0x10
is 16
. We can use the letters a-f,
as is typical in many other programming languages; 0xdeadbeef
is valid. The prefix 0o
(zero and the letter o) is used for base eight; try to avoid using the maliciously confusing 0O
(zero and capital O) for octal values, for example, 0o33653337357
. We can write base two literal values using the 0b
prefix: 0b10
is 2
. The most common use case for non-decimal numbers is providing hexadecimal values for an array of bytes, and this is relatively rare.
Using the bit-oriented operators
Bit-oriented operators are defined for integers. They're not defined for complex or floating-point objects.
The <<
and >>
operators perform bit shifting. 1 << 8
, for example, is 256. We've shifted the value 1 to the left 8 bit positions.
The &
, |
, and ^
operators compute the bitwise "and", bitwise "or", and bitwise "xor" of two integer values. Here are some examples:
>>> 9 & 5 1 >>> 9 | 5 13 >>> 9 ^ 3 10
To visualize these operators, we can use the bin()
function to see the binary values involved.
>>> bin(9) '0b1001' >>> bin(5) '0b101'
Using the bin()
function can clarify how the bits of 9|5
combine to create the bits of 13
. The ~
operator is the bitwise two's complement of an integer value. ~14
, for example, is -15
. These are emphatically not logical operators. Logical operators are described in Chapter 5, Logic, Comparisons, and Conditions.
Tip
Do not confuse a & b
with a and b
:
a & b
computes a bitwise "and" of the bits in the integers a and b.a and b
computes the Boolean "and" based on the truth values of a and b.
Using rational numbers
Rational numbers are fractions composed of two integer values. Python doesn't have a built-in rational number type. We must import the Fraction
class using this:
>>> from fractions import Fraction
This will introduce the Fraction
class definition to our global environment. Once we have this, we can create objects of the class Fraction
as follows:
>>> Fraction(355,113) Fraction(355, 113)
Arithmetic and comparison operators apply to fractions. When doing mixed-type expressions, fractions fit into the numeric tower above integers and below floating-point values. Here's an example of an integer coerced to a fraction:
>>> Fraction(4,2)*3 Fraction(6, 1)
Performing an operation that involves a Fraction
value and an int
value requires that the int
object is coerced up to the Fraction
class.
We can extract the numerator and denominator of a fraction using their attribute names. Here's an example:
>>> a= Fraction(355,113)*5 >>> a.numerator 1775 >>> a.denominator 113
We've created a Fraction
object, a
, from an expression involving a Fraction
object and an integer. We've then extracted the numerator
and denominator
attributes of the variable a
.
Using decimal numbers
For currency calculations, we generally use Decimal
numbers. Python doesn't have a built-in decimal number type. We import the Decimal
class using this:
>>> from decimal import Decimal
This will introduce the Decimal
class definition to our global environment. We can now create Decimal
objects. It's important to avoid accidentally mixing Decimal
and float
values, because float
values are only an approximation. To be sure that Decimal
values are exact, we must use only integers or strings.
>>> Decimal("2.72") Decimal('2.72')
We've created a Decimal
value from a string. The resulting Decimal
object will represent this exactly, carefully preserving the appropriate decimal places and rounding up or down as required. For common financial calculations, Decimal
is required. Here's an example:
>>> (Decimal('512.97')+Decimal('5.97'))*Decimal('0.075') Decimal('38.92050')
We've added two prices, $512.97 and $5.97 and computed a sales tax of 7.5%. The tax is $38.92050, to be precise. This is generally rounded to $38.92.
If we try this kind of financial calculation with floating-point values, we have a bit of a problem:
>>> (512.97+5.97)*0.075 38.920500000000004
The floating-point approximations don't produce an exact answer.
Python coercion rules work well with Decimal
and int
values. We can calculate Decimal('3.99')*3
and get Decimal('11.97')
as the answer.
The coercion rules aren't implemented by the Decimal
and float
classes. It might make some sense for Decimal
values to be coerced up to float
values. On the other hand, this might indicate a profound programming error when mixing exact currency values and floating-point approximations. Since this is ambiguous, and debatable, the general approach followed by Python is summarized by this line from Tim Peters' The Zen of Python:
In the face of ambiguity, refuse the temptation to guess.
Consequently, mixing Decimal
and float
leads to TypeError
exceptions instead of following coercion up the numeric tower and switching from exact to approximate values. We must explicitly convert Decimal
to float
to do mixed-type expressions.
Using floating-point numbers
Floating-point values are instances of the class float
. These objects work with arithmetic and comparison operators. They don't participate in the bit-oriented operators.
The details of Python floating-point implementations can vary. CPython depends on the standard C libraries, which should provide reasonably consistent results on a wide variety of hardware and OS platforms. C libraries generally use IEEE 754 floating-point values; Python's float
type is the C language double
. This means that a float will be a 64-bit value with (effectively) a 53-bit fraction and an 11-bit exponent. The exponent range is from to .
We can write floating-point numbers two ways: as digits with a decimal point, as well as in "scientific" notation:
>>> 6335.437 6335.437 >>> 6.335437E3 6335.437
The E
notation shows a power of 10. That means 6.335437E3 is .
It's very important to note that floating-point values are an approximation. We can't emphasize enough that they're not exact and should not be used for currency calculations. Here's an example of what happens when working with floating-point approximations:
>>> (5**6)**(1/6) 4.999999999999999
This should not be surprising in any way. Mathematically, . Since values like 1/6 don't have exact binary representations, this kind of expression reveals the consequences of working with approximations.
The fact that floating-point numbers use a binary representation leads to interesting complications. A number such as 1/6 has no exact decimal representation; we can use .1666... to indicate that the decimal positions repeat infinitely. However, a number such as 1/5 has an exact decimal representation, 0.2. Neither of these numbers has an exact binary representation. Since we must use a finite number of bits, we'll notice slight discrepancies between idealized values and the finite values produced on a digital computer.
Note that exact equality comparisons between floating-point numbers, while permitted, is generally not a good idea. In Chapter 5, Logic, Comparisons, and Conditions, we'll address how to use a narrow range instead of exact equality. Instead of a == b
, we need to focus on abs(a-b) < ε
.
Using complex numbers
The top of Python's number tower is of the complex
type. It can be thought of as expressions built from a pair of floating-point numbers: one is a real value, the other is an imaginary value. The imaginary value is multiplied by . We write (2+3j)
to mean .
When working with complex numbers, we often import the cmath
library instead of the math
library. The math.sqrt()
function is constrained to work only with float
values, and will raise an exception rather than provide an imaginary value. The cmath.sqrt()
function will provide a proper imaginary value, if required.
This library shows us that is essentially true:
>>> cmath.e**(cmath.pi*1j)+1 1.2246467991473532e-16j
Note that we used 1j
to represent . If we try to use the identifier j
(without a number in front of it) it is seen as a simple variable. The value 1j
is a complex literal because it starts with a digit and ends with j
.
Since floating-point values have about 53 bits, which is about 16 decimal digits, we can expect float
approximations of irrational values like π
and e
to be off by about .