Advanced Expressions

The math and random Modules, Bit-Level Operations, Division

This chapter covers some more advanced topics. The math Module cover the math module. The The random Module covers elements of the random module.

Division Operators covers the important distinction between the division operators. We also provide some supplemental information that is more specialized. Bit Manipulation Operators covers some additional bit-fiddling operators that work on the basic numeric types. Expression Style Notes has some notes on style.

Using Modules

A Python module extends the Python execution environment by adding new classes, functions and helpful constants. We tell the Python interpreter to fetch a module with a variation on the import statement. There are several variations on import, which we’ll cover in depth in Components, Modules and Packages.

For now, we’ll use the simple import:

import m

This will import module m. Only the module’s name, m is made available. Every name inside the module m must be qualified by prepending the module name and a .. So if module m had a function called spam(), we’d refer to it as m.spam().

There are dozens of standard Python modules. We’ll get to the most important ones in Components, Modules and Packages. For now, we’ll focus on extending the math capabilities of the basic expressions we’ve looked so far.

The math Module

The math module is made available to your programs with:

import math

The math module contains a number of common trigonometric functions.

math.acos(x) → number
Arc cosine of x; result in radians.
math.asin(x) → number
arc sine of x; result in radians.
math.atan(x) → number
arc tangent of x; result in radians.
math.atan2(y, x) → number
arc tangent of y \div x: arctan( \frac{y}{x} ); result in radians.
math.cos(x) → number`
cosine of x in radians.
math.cosh(x) → number
hyperbolic cosine of x in radians.
math.exp(x) → number
e^x, inverse of log(x).
math.hypot(x, y) → number
Euclidean distance, \sqrt{ x^2 + y^2 }; the length of the hypotenuse of a right triangle with height of :replaceable:y` and length of x.
math.log(x) → number
Natural logarithm (base e) of x. Inverse of exp(). n = e^{\ln{n}}.
math.log10(x) → number
natural logarithm (base 10) of x , inverse of 10** x. n = 10^{\log{n}}.
math.pow(x, y) → number
x^y.
math.sin(x) → number
sine of x in radians.
math.sinh(x) → number
hyperbolic sine of x in radians.
math.sqrt(x) → number
square root of x. This version returns an error if you ask for sqrt(-1), even though Python understands complex and imaginary numbers. A second module, cmath, includes a version of sqrt() which correctly creates imaginary numbers.
math.tan(x) → number
tangent of x in radians.
math.tanh(x) → number
hyperbolic tangent of x in radians.

Additionally, the following constants are also provided.

math.pi:the value of \pi, 3.1415926535897931
math.e:the value of e, 2.7182818284590451, used for the exp() and log() functions.

Conversion between radians, r, and degrees, d, is based on the following definition:

360 \text{ degrees} = 2 \times \pi \text{ radians}

From that, we get the following relationships:

d \times \pi = r \times 180

d = \frac{r \times 180}{\pi}, r = \pi \times \frac{d}{180}

The math module contains the following other functions for dealing with floating point numbers.

math.ceil(x) → number

Next larger whole number.

>>> import math
>>> math.ceil(5.1)
6.0
>>> math.ceil(-5.1)
-5.0
math.fabs(x) → number
Absolute value of the real x.
math.floor(x) → number

Next smaller whole number.

>>> import math
>>> math.floor(5.9)
5.0
>>> math.floor(-5.9)
-6.0
math.fmod(x, y) → number

Floating point remainder after division of \lfloor x \div y \rfloor. This depends on the platform C library and may handle the signs differently than the Python x % y.

>>> math.fmod( -22, 7 )
-1.0
>>> -22 % 7
6
math.modf(x) → tuple

Creates a tuple with the fractional and integer parts of x. Both results carry the sign of x so that x can be reconstructed by adding them. We’ll return to tuples in Tuples.

>>> math.modf( 123.456 )
(0.45600000000000307, 123.0)
math.frexp(x) → tuple
This function unwinds the usual base-2 floating point representation. A floating point number is m \times 2^e, where m is always a fraction \frac{1}{2} \leq m \leq 1, and e is an integer. This function returns a tuple with m and e. The inverse is ldexp(m,e).
math.ldexp(m, e) → number
Calculat m \times 2^e, the inverse of frexp(x).

The random Module

The random module contains a large number of functions for working with distributions of random numbers. There are numerous functions available, but the later exercises will only use these functions.

The random module is made available to your program with:

import random

Here are the definitions of some commonly-used functions.

random.choice(sequence) → value

Chooses a random value from the sequence sequence.

>>> import random
>>> random.choice( ['red', 'black', 'green'] )
'red'
random.random() → number
A random floating point number, r, such that 0 \leq r < 1.0.
random.randrange([start], stop[, step]) → number

Choose a random element from range( start, stop, step ).

  • randrange(6) returns a number, r, such that 0 \leq r < 6. There are 6 values between 0 and 5.
  • randrange(1,7) returns a number, r, such that 1 \leq r < 7. There are 6 values between 1 and 6.
  • randrange(10,100,5) returns a number, such that 10 \leq 5k < 100. for some integer value of k. These are values 10, 15, 20, ..., 95.
random.randint(a, b) → number
Choose a random number, r, such that a \leq r \leq b. Unlike randrange(), this function includes both end-point values.
random.uniform(a, b) → number
Returns a random floating point number, r, such that a \leq r < b.

The randrange() has two optional values, making it particularly flexible. Here’s an example of some of the alternatives.

demorandom.py

#!/usr/bin/env python
import random
# Simple Range 0 <= r < 6
print random.randrange(6), random.randrange(6)
# More complex range 1 <= r < 7
print random.randrange(1,7), random.randrange(1,7)
# Really complex range of even numbers between 2 and 36
print random.randrange(2,37,2)
# Odd numbers from 1 to 35
print random.randrange(1,36,2)

This demonstrates a number of ways of generating random numbers. It uses the basic random.randrange() with a variety of different kinds of arguments.

Advanced Expression Exercises

  1. Evaluate These Expressions. The following expressions are somewhat more complex, and use functions from the math module.

    math.sqrt( 40.0/3.0 - math.sqrt(12.0) )

    6.0/5.0*( (math.sqrt(5)+1) / 2 )**2

    math.log( 2198 ) / math.sqrt( 6 )

  2. Run demorandom.py. Run the demorandom.py script several times and save the results. Then add the following statement to the script and run it again several times. What happens when we set an explicit seed?

    #!/usr/bin/env python
    import random
    random.seed(1)
    ...everything else the same
    

    Try the following variation, and see what it does.

    #!/usr/bin/env python
    import random, time
    random.seed(time.clock())
    ...everything else the same
    
  3. Wind Chill. Wind chill is used by meteorologists to describe the effect of cold and wind combined.

    Given the wind speed in miles per hour, V, and the temperature in °F, T, the Wind Chill, w, is given by the formula below.

    Wind Chill, new model

    35.74 + 0.6215 \times T - 35.75 \times (V^{0.16}) + 0.4275 \times T \times (V^{0.16})

    Wind Chill, old model

    0.081 \times ( 3.71 \times \sqrt{V} + 5.81 - 0.25 \times V ) \times (T - 91.4) + 91.4

    Wind speeds are for 0 to 40 mph, above 40, the difference in wind speed doesn’t have much practical impact on how cold you feel.

    Write a print statement to compute the wind chill felt when it is -2 °F and the wind is blowing 15 miles per hour.

  4. How Much Does The Atmosphere Weigh? Part 1 From Slicing Pizzas, Racing Turtles, and Further Adventures in Applied Mathematics, [Banks02].

    Pressure is measured in Newtons, N, \text{kg}\cdot\text{m} / \text{sec}^2. Air Pressure is is measured in Newtons of force per square meter, \text{N} / \text{m}^2.

    Air Pressure (at sea level) P_0. This is the long-term average.

    P_0 = 1.01325 \times 10^5

    Acceleration is measured in \text{m}/\text{sec}^2. Gravity acceleration (at sea level) g.

    g = 9.82

    We can use g to get the kg of mass from the force of air pressure P_0. Apply the acceleration of gravity (in m/sec^2) to the air pressure (in \text{kg}\cdot\text{m}/\text{sec}^2). This result is mass of the atmosphere in kilograms per square meter (\text{kg}/\text{m}^2).

    M_{m^2} = P_0 \times g

    Given the mass of air per square meter, we need to know how many square meters of surface to apply this mass to.

    Radius of Earth R in meters, m. This is an average radius; our planet isn’t a perfect sphere.

    R = 6.37 \times 10^6

    The area of a Sphere.

    A = 4 \pi r^2

    Mass of atmosphere (in Kg) is the weight per square meter, times the number of square meters.

    M_a = P_0 \times g \times A

    Check: somewhere around 10^{18} kg.

  5. How Much Does The Atmosphere Weigh? Part 2. From Slicing Pizzas, Racing Turtles, and Further Adventures in Applied Mathematics, [Banks02].

    The exercise How Much Does the Atmosphere Weigh, Part 1 assumes the earth to be an entirely flat sphere. The averge height of the land is actually 840m. We can use the ideal gas law to compute the pressure at this elevation and refine the number a little further.

    Pressure at a given elevation

    P = P_0 \times e^{\frac{mg}{RT}z}

    Molecular weight of air m = 28.96 \times 10^{-3} \text{kg}/\text{mol}.

    m = 28.96 \times 10^{-3}

    Gas constant R, in \text{joule}/(\text{K} \cdot \text{mol}).

    R = 8.314

    Gravity g, in \text{m}/\text{sec}^2.

    g = 9.82

    Temperature T, in °K based on temperature C, in °C. We’ll just assume that C is 15 °C.

    T = 273 + C

    Elevation z, in meters, m.

    z = 840

    This pressure can be used for the air over land, and the pressure computed in How Much Does the Atmosphere Weigh, Part 1 can be used for the air over the oceans. How much land has this reduced pressure? Reference material gives the following areas in m^2, square meters.

    ocean area: A_o = 3.61 \times 10^{14}

    land area: A_l = 1.49 \times  10^{14}

    Weight of Atmosphere, adjusted for land elevation

    M_l = P_0 \times g \times A_0 + P \times g \times A_l

Bit Manipulation Operators

We’ve already seen the usual math operators: +, -, *, /, %, **; as well as the abs() and pow() functions. There are several other operators available to us. Principally, these are for manipulating the individual bits of an integer value.

We’ll look at ~, &, ^, |, << and >>.

The unary ~ operator flops all the bits in a plain or long integer. 1’s become 0’s and 0’s become 1’s. Since most hardware uses a technique called 2’s complement, this is mathematically equivalent to adding 1 and switching the number’s sign.

>>> print ~0x12345678
-305419897

There are binary bit manipulation operators, also. These perform simple Boolean operations on all bits of the integer at once.

The binary & operator returns a 1-bit if the two input bits are both 1.

>>> print 0&0, 1&0, 1&1, 0&1
0 0 1 0

Here’s the same kind of example, combining sequences of bits. This takes a bit of conversion to base 2 to understand what’s going on.

>>> print 3&5
1

The number 3, in base 2, is 0011. The number 5 is 0101. Let’s match up the bits from left to right:

  0 0 1 1
& 0 1 0 1
  -------
  0 0 0 1

The binary ^ operator returns a 1-bit if one of the two inputs are 1 but not both. This is sometimes called the exclusive or.

>>> print 3^5
6

Let’s look at the individual bits

  0 0 1 1
^ 0 1 0 1
  -------
  0 1 1 0

Which is the binary representation of the number 6.

The binary | operator returns a 1-bit if either of the two inputs is 1. This is sometimes called the inclusive or. Sometimes this is written and/or.

>>> print 3|5
7

Let’s look at the individual bits.

  0 0 1 1
| 0 1 0 1
  -------
  0 1 1 1

Which is the binary representation of the number 7.

There are also bit shifting operations. These are mathematically equivalent to multiplying and dividing by powers of two. Often, machine hardware can execute these operations faster than the equivalent multiply or divide.

The << is the left-shift operator. The left argument is the bit pattern to be shifted, the right argument is the number of bits.

>>> print 0xA << 2
40

0xA is hexadecimal; the bits are 1-0-1-0. This is 10 in decimal. When we shift this two bits to the left, it’s like multiplying by 4. We get bits of 1-0-1-0-0-0. This is 40 in decimal.

The >> is the right-shift operator. The left argument is the bit pattern to be shifted, the right argument is the number of bits. Python always behaves as though it is running on a 2’s complement computer. The left-most bit is always the sign bit, so sign bits are shifted in.

>>> print 80 >> 3
10

The number 80, with bits of 1-0-1-0-0-0-0, shifted right 3 bits, yields bits of 1-0-1-0, which is 10 in decimal.

There are some other operators available, but, strictly speaking, they’re not arithmetic operators, they’re logic operations. We’ll return to them in Truth, Comparison and Conditional Processing.

Division Operators

In general, the data type of an expresion depends on the types of the arguments. This rule meets our expectations for most operators: when we add two integers, the result should be an integer. However, this doesn’t work out well for division because there are two different expectations. Sometimes we expect division to create precise answers, usually the floating-point equivalents of fractions. Other times, we want a rounded-down integer result.

The classical Python definition of / followed the pattern for other operators: the results depend entirely on the arguments. 685/252 was 2 because both arguments where integers. However, 685./252. was 2.7182539682539684 because the arguments were floating point.

This definition often caused problems for applications where data types were used that the author hadn’t expected. For example, a simple program doing Celsius to Fahrenheit conversions will produce different answers depending on the input. If one user provides 18 and another provides 18.0, the answers were different, even though all of the inputs all had the equal numeric values.

>>> 18*9/5+32
64
>>> 18.0*9/5+32
64.400000000000006
>>> 18 == 18.0
True

This unexpected inaccuracy was generally due to the casual use of integers where floating-point numbers were more appropriate. (This can also occur using integers where complex numbers were implictly expected.) An explicit conversion function (like float()) can help prevent this. The idea, however, is for Python be a simple and sparse language, without a dense clutter of conversions to cover the rare case of an unexpected data type.

Starting with Python 2.2, a new division operator was added to clarify what is required. There are two division operators: / and //. The / operator should return floating-point results; the // operator will always return rounded-down results.

In Python 2.5 and 2.6, the / operator can either use “classical” or “old” rules (results depend on the values) or it can use the “new” rule (result is floating-point.) In Python 3.x, this transitional meaning of / goes away and it always produces a floating-point result.

Important

Python 3

In Python 3, the / operator will always produces a floating-point result. The // operator will continue to produce an integer result.

To help with the transition, two tools were made available. This gives programmers a way to keep older applications running; it also gives them a way to explicitly declare that their program uses the newer operator definition. There are two parts to this: a program statememt that can be placed in a program, as well as command-line options that can be used when starting the Python interpreter.

Program Statements. To ease the transition from older to newer language features, there is a __future__ module available. This module includes a division definition that changes the definition of the / operator from classical to future. You can include the following import statement to state that your program depends on the future definition of division. We’ll look at the import statement in depth in Components, Modules and Packages.

from __future__ import division
print 18*9/5+32
print 18*9//5+32

This produces the following output. The first line shows the new use of the / operator to produce floating point results, even if both arguments are integers. The second line shows the // operator, which produces rounded-down results.

64.4
64

The from __future__ statement will set the expectation that your script uses the new-style floating-point division operator. This allows you to start writing programs with version 2.6 that will work correctly with all future versions. By version 3.0, this import statement will no longer be necessary, and these will have to be removed from the few modules that used them.

Command Line Options. Another tool to ease the transition are command-line options used when running the Python interpreter. This can force old-style interpretation of the / operator or to warn about old-style use of the / operator between integers. It can also force new-style use of the / operator and report on all potentially incorrect uses of the / operator.

The Python interpreter command-line option of -Q will force the / operator to be treated classically (“old”), or with the future (“new”) semantics. If you run Python with -Qold , the / operator’s result depends on the arguments. If you run Python with -Qnew, the / operator’s result will be floating point. In either case, the // operator returns a rounded-down integer result.

You can use -Qold to force old modules and programs to work with version 2.2 and higher. When Python 3.0 is released, however, this transition will no longer be supported; by that time you should have fixed your programs and modules.

To make fixing easier, the -Q command-line option can take two other values: warn and warnall . If you use -Qwarn, then the / operator applied to integer arguments will generate a run-time warning. This will allow you to find and fix situations where the // operator might be more appropriate. If you use -Qwarnall, then all instances of the / operator generate a warning; this will give you a close look at your programs.

You can include the command line option when you run the Python interpreter. For Linux and MacOS users, you can also put this on the #! line at the beginning of your script file.

#!/usr/local/bin/python -Qnew

Table Of Contents

Previous topic

Simple Numeric Expressions and Output

Next topic

Variables, Assignment and Input

This Page