This chapter is taken from the book A Primer on Scientific Programming with Python by H. P. Langtangen, 5th edition, Springer, 2016.
Our next example involves the formula for converting temperature measured in Celsius degrees to the corresponding value in Fahrenheit degrees: $$ \begin{equation} F = \frac{9}{5}C + 32 \tag{3} \end{equation} $$ In this formula, \( C \) is the amount of degrees in Celsius, and \( F \) is the corresponding temperature measured in Fahrenheit. Our goal now is to write a computer program that can compute \( F \) from (3) when \( C \) is known.
A straightforward attempt at coding the formula (3) goes as follows:
C = 21
F = (9/5)*C + 32
print F
The parentheses around 9/5
are not strictly needed, i.e.,
(9/5)*C
is computationally identical to 9/5*C
, but
parentheses remove any doubt that 9/5*C
could mean
9/(5*C)
. The section Arithmetic operators and precedence has more
information on this topic.
When run under Python version 2.x, the program prints the value
53. You can find the program in the file c2f_v1.py in the src/formulas
folder in the
folder tree of example programs from this document (downloaded from
http://hplgit.github.com/scipro-primer). The v1
part of the
name stands for version 1. Throughout this document, we will often
develop several trial versions of a program, but remove the version
number in the final version of the program.
Testing the correctness is easy in this case since we can evaluate the formula on a calculator: \( \frac{9}{5}\cdot 21+32 \) is 69.8, not 53. What is wrong? The formula in the program looks correct!
The error in our program above is one of the most common errors in mathematical software and is not at all obvious for a newcomer to programming. In many computer languages, there are two types of divisions: float division and integer division. Float division is what you know from mathematics: 9/5 becomes 1.8 in decimal notation.
Integer division \( a/b \) with integers (whole numbers) \( a \) and \( b \)
results in an integer that is truncated (or mathematically, rounded
down). More precisely, the result is the largest integer \( c \) such that
\( bc\leq a \). This implies that \( 9/5 \) becomes 1 since \( 1\cdot 5 = 5 \leq
9 \) while \( 2\cdot 5 = 10 >9 \). Another example is \( 1/5 \), which becomes 0
since \( 0\cdot 5 \leq 1 \) (and \( 1\cdot 5 > 1 \)). Yet another example is
\( 16/6 \), which results in 2 (try \( 2\cdot 6 \) and \( 3\cdot 6 \) to convince
yourself). Many computer languages, including Fortran, C, C++, Java,
and Python version 2, interpret a division operation a/b
as integer
division if both operands a
and b
are integers. If either a
or
b
is a real (floating-point) number, a/b
implies the standard
mathematical float division. Other languages, such as MATLAB and
Python version 3, interprets a/b
as float division even if both
operands are integers, or complex division if one of the operands is a
complex number.
The problem with our program is the coding of the formula (9/5)*C +
32
. This formula is evaluated as follows. First, 9/5
is
calculated. Since 9
and 5
are interpreted by Python as integers
(whole numbers), 9/5
is a division between two integers, and Python
version 2 chooses by default integer division, which results in
1. Then 1 is multiplied by C
, which equals 21, resulting in
21. Finally, 21 and 32 are added with 53 as result.
We shall very soon present a correct version of the temperature conversion program, but first it may be advantageous to introduce a frequently used term in Python programming: object.
When we write
C = 21
Python interprets the number 21
on the right-hand side of the
assignment as an integer and creates an int
(for integer) object
holding the value 21. The variable C
acts as a name for this int
object. Similarly, if we write C = 21.0
, Python recognizes 21.0
as a real number and therefore creates a float
(for floating-point)
object holding the value 21.0 and lets C
be a name for this
object. In fact, any assignment statement has the form of a variable
name on the left-hand side and an object on the right-hand side. One
may say that Python programming is about solving a problem by defining
and changing objects.
At this stage, you do not need to know what an object really is, just
think of an int
object as a collection, say a storage box, with some
information about an integer number. This information is stored
somewhere in the computer's memory, and with the name C
the program
gets access to this information. The fundamental issue right now is
that 21 and 21.0 are identical numbers in mathematics, while in a
Python program 21 gives rise to an int
object and 21.0 to a float
object.
There are lots of different object types in Python, and you will later learn how to create your own customized objects. Some objects contain a lot of data, not just an integer or a real number. For example, when we write
print 'A text with an integer %d and a float %f' % (2, 2.0)
a str
(string) object, without a name,
is first made of the text between the quotes
and then this str
object is printed. We can alternatively
do this in two steps:
s = 'A text with an integer %d and a float %f' % (2, 2.0)
print s
As a quite general rule of thumb, one should be careful to avoid
integer division when programming mathematical formulas. In the rare
cases when a mathematical algorithm does make use of integer division,
one should use a double forward slash, //
, as division operator,
because this is Python's way of explicitly indicating integer
division.
Python version 3 has no problem with unintended integer division, so
the problem only arises with Python version 2 (and many other common
languages for scientific computing). There are several ways to avoid
integer division with the plain /
operator. The simplest remedy in
Python version 2 is to write
from __future__ import division
This import statement must be present in the beginning of every file
where the /
operator always shall imply float division.
Alternatively, one can run a Python program someprogram.py
from the
command line with the argument -Qnew
to the Python interpreter:
Terminal> python -Qnew someprogram.py
A more widely applicable method, also in other programming languages
than Python version 2, is to enforce one of the operands to be a
float
object. In the current example, there are several ways to do
this:
F = (9.0/5)*C + 32
F = (9/5.0)*C + 32
F = float(C)*9/5 + 32
In the first two lines, one of the operands is written as a decimal
number, implying a float
object and hence float division. In the
last line, float(C)*9
means float
times int
, which results in a
float
object, and float division is guaranteed.
A related construction,
F = float(C)*(9/5) + 32
does not work correctly, because 9/5
is evaluated by integer
division, yielding 1, before being multiplied by a float
representation of C
(see next section for how compound arithmetic
operations are calculated). In other words, the formula reads
F=C+32
, which is wrong.
We now understand why the first version of the program does not work and what the remedy is. A correct program is
C = 21
F = (9.0/5)*C + 32
print F
Instead of 9.0
we may just write 9.
(the dot implies a float
interpretation of the number). The program is available in the file
c2f.py. Try to run it - and observe
that the output becomes 69.8, which is correct.
Running a Python program with the -Qwarnall
argument, say
Terminal> python -Qwarnall someprogram.py
will print out a warning every time an integer division
expression is encountered in Python version 2.
We could easily have run into problems in our very first programs if
we instead of writing the formula \( \frac{1}{2}gt^2 \) as 0.5*g*t**2
wrote (1/2)*g*t**2
. This term would then always be zero!
Formulas in Python programs are usually evaluated in the same way as
we would evaluate them mathematically. Python proceeds from left to
right, term by term in an expression (terms are separated by plus or
minus). In each term, power operations such as \( a^b \), coded as
a**b
, has precedence over multiplication and division. As in
mathematics, we can use parentheses to dictate the way a formula is
evaluated. Below are two illustrations of these principles.
5/9+2*a**4/2
: First 5/9 is evaluated (as integer division, giving 0 as result), then \( a^4 \) (a**4
) is evaluated, then 2 is multiplied with \( a^4 \), that result is divided by 2, and the answer is added to the result of the first term. The answer is therefore a**4
.5/(9+2)*a**(4/2)
: First \( 5\over 9+2 \) is evaluated (as integer division, yielding 0), then 4/2 is computed (as integer division, yielding 2), then a**2
is calculated, and that number is multiplied by the result of 5/(9+2)
. The answer is thus always zero.