# Better Arithmetic Through Functions¶

Beyond Add, Subtract, Multiply and Divide

We’ve seen one function, abs(), that is also a standard mathematical function. The usual mathematical notation is . Some mathematical functions are difficult to represent with simple lines of text, so the folks who invented Python elected to use “prefix” notation, putting the name of the function first.

This function syntax is pervasive in Python, and we’ll see many operations that are packaged in the form of functions. We’ll look at many additional function definitions throughout this book. In this chapter, we’ll focus on built-in functions.

We’ll look at a few basic functions in Say It With Functions; we’ll show how formal definitions look in pow() and round() Definitions. We’ll show how you can evaluate complex expressions in Multiple Steps. We’ll touch in the accuracy issue in Accuracy?. We’ll look at how Python gives you flexibility through optional features in Another Round, Please.

There are a number of conversion or factory functions that we’ll describe in Functions are Factories (really!). In Going the Other Way we’ll see how we can use conversion functions to make strings from numbers. Finally, in Most and Least, we’ll look at functions to find the maximum or minimum of a number of values.

## Say It With Functions¶

Many of the Python processing operations that we might need are provided in the form of functions. Functions are one of the ways that Python lets us specify how to process some data. A function, in a mathematical sense, is a transformation from some input to an output. The mathematicians sometimes call this a mapping, because the function is a kind of map from the input value to the output value.

We looked at the abs() function in the previous section. It maps negative and positive numbers to their absolute magnitude, measured as a positive number. The abs() function maps -4 to 4, and 3.25 to 3.25.

We’ll start out looking at two new mathematical functions, pow() and round(). Here are some examples of abs(), pow() and round().

```>>> abs(-18)
18
>>> pow(16,3)
4096
>>> round(9.424)
9.0
>>> round(12.57)
13.0
```

A function is an expression, with the same syntactic role as any other expression, for example 2+3. You can freely combine functions with other expressions to make more complex expressions. Additionally, the arguments to a function can also be expressions. Therefore, we can combine functions into more complex expressions pretty freely. This takes some getting used to, so we’ll look at some examples.

```>>> 3*abs(-18)
54
>>> pow(8*2,3)*1.5
6144.0
>>> round(66.2/7)
9.0
>>> 8*round(abs(50.25)/4.0,2)
100.48
```
1. In the first example, Python has to compute a product. To do this, it must first compute the absolute value of -18. Then it can multiply the absolute value by 3.
2. In the second example, Python has to compute a product of a pow() function and 1.5. To do this, it must first compute the product of 8 times 2 so that it can raise it to the 3rd power. This is then multiplied by 1.5. You can see that first Python evaluates any expressions that are arguments to the function, then it evaluates the function. Finally, it evaluates the overall expression in which the function occurs.
3. In the third example, Python computes the quotient of 66.2 and 7, and then rounds this to the nearest whole number.
4. Finally, the fourth example does a whopping calculation that involves several steps. Python has to find the absolute value of 50.25, divide this by 4, round that answer off to two positions and then multiply the result by 8. Whew!

## pow() and round() Definitions¶

The function names provide a hint as to what they do. Here are the formal definitions, the kind of thing you’ll see in the Python reference manuals.

pow(x, y[, z]) → number

Raises x to the y power.

If z is present, this is done modulo z: .

round(number[, ndigits]) → number
Rounds number to ndigits to the right of the decimal point.

The [ and ]‘s are how we show that some parts of the syntax are optional. We’ll summarize this in Function Syntax Rules.

Important

Function Syntax Rules

We’ll show optional parameters to functions by surrounding them with [ and ]. We don’t actually enter the [ and ]‘s; they’re just hints as to what the alternative forms of the function are.

round(number[, ndigits]) → number
Rounds number to ndigits to the right of the decimal point.

In the case of the round() function, the syntax summary shows us there are two different ways to use this function:

• We can use round() with the one required parameter, number. Example: round( 3.14159 )
• We can use round() with two parameters, number and ndigits. Example: round( 3.14159, 2 )
```>>> round( 2.459, 2 )
2.46
>>> round( 2.459 )
2.0
```

Note that there is some ambiguity between using [ and ] in our Python programming and using [ and ] as markup for the grammar rules. Usually the context makes it clear.

## Multiple Steps¶

We can use the pow() function for the same purpose as the ** operator. Here’s an example of using pow() instead of x ** y.

```>>> 2L**32
4294967296L
>>> pow(2L,32)
4294967296L
```

Note that pow(x,0.5) is the square root of x. Also, the function math.sqrt() is the square root of x. The pow() function is one of the built-in functions, while the square root function is only available in the math library. We’ll look at the math library in The math Module – Trig and Logs.

In the next example we’ll get the square root of a number, and then square that value. It’ll be a two-step calculation, so we can see each intermediate step.

```>>> pow( 2, 0.5 )
1.4142135623730951
>>> _ ** 2
2.0000000000000004
```

The first question you should have is “what does that _ mean?”

The _ is a Python short-cut. During interactive use, Python uses the name _ to mean the result it just printed. This saves us retyping things over and over. In the case above, the “previous result” was the value of pow( 2, 0.5 ). By definition, we can replace a _ with the entire previous expression to see what is really happening.

```>>> pow( 2, 0.5 ) ** 2
2.0000000000000004
```

Until we start writing scripts, this is a handy thing. When we start writing scripts, we won’t be able to use the _, instead we’ll use something that’s a much more clear and precise.

## Accuracy?¶

Let’s go back to the previous example: we’ll get the square root of a number, and then square that value.

```>>> pow( 3, 0.5 )
1.7320508075688772
>>> _ ** 2
2.9999999999999996
>>> pow( 3, 0.5 ) ** 2
2.9999999999999996
```

Here’s a big question: what is that “.9999999999999996” foolishness?

That’s the left-overs from the conversion from our decimal notation to the computer’s internal binary and back to human-friendly decimal notation. We talked about it briefly in Floating-Point Numbers, Also Known As Scientific Notation. If we know the order of magnitude of the result, we can use the round function to clean up this kind of small error. In this case, we know the answer is supposed to be a whole number, so we can round it off.

```>>> pow( 3, 0.5 ) ** 2
2.9999999999999996
>>> round(_)
3.0
```

Tip

Debugging Function Expressions

If you look back at Syntax Rule 6, you’ll note that the ()‘s need to be complete. If you accidentally type something like round(2.45 with no closing ), you’ll see the following kind of exchange.

```>>> round(2.45
...
...
... )
2.0
```

The ... is Python’s hint that the statement is incomplete. You’ll need to finish the ()‘s so that the statement is complete.

## Another Round, Please¶

Above, we noted that the round() function had an optional argument. When something’s optional, we can look at it as if there are two forms of the round() function: a one-argument version and a two-argument version.

• The one-argument round() function rounds a number to the nearest whole number.
• If you provide the optional second parameter, this is the number of decimal places to round to. If the number of decimal places is a positive number, this is decimal places to the right of the decimal point. If the number of decimal places is a negative number, this is the number of places to the left of the decimal point.
```>>> round(678.456)
678.0
>>> round(678.456,2)
678.46000000000004
>>> round(678.456,-1)
680.0
```

So, rounding off to -1 decimal places means the nearest 10. Rounding off to -2 decimal places is the nearest 100. Pretty handy for doing business reports where we have to round off to the nearest million.

## Functions are Factories (really!)¶

How do we get Python to do specific conversions among our various numeric data types? When we mix whole numbers and floating-point scientific notation, Python automatically converts everything to floating-point. What if we want the floating-point number truncated down to a whole number instead?

Here’s another example: what if we want the floating-point number transformed into a long integer instead of the built-in assumption that we want long integers turned into floating-point numbers? How do we control this coercion among numbers?

We’ll look at a number of factory functions that do number conversion. Each function is a factory that creates a new number from an existing number. Eventually, we’ll identify numerous varieties of factory functions.

These factory functions will also create numbers from string values. When we write programs that read their input from files, we’ll see that files mostly have strings. Factory functions will be an important part of reading strings from files and creating numbers from those strings so that we can process them.

float(x) → number
Creates a floating-point number equal to the string or number x. If a string is given, it must be a valid floating-point number: digits, decimal point, and an exponent expression. You can use this function when doing division to prevent getting the simple integer quotient.

For example:

```>>> float(22)/7
3.1428571428571428
>>> 22/7
3
>>> float("6.02E24")
6.0200000000000004e+24
```
int(x) → number
Creates an integer equal to the string or number x. This will chop off all of the digits to the right of the decimal point in a floating-point number. If a string is given, it must be a valid decimal integer string.
```>>> int('1234')
1234
>>> int(3.14159)
3
```
long(x) → number
Creates a long integer equal to the string or number x. If a string is given, it must be a valid decimal integer. The expression long(2) has the same value as the literal 2L. Examples: long(6.02E23), long(2).
```>>> long(2)**64
18446744073709551616L
>>> long(22.0/7.0)
3L
```

The first example shows the range of values possible with 64-bit integers, available on larger computers. This is a lot more than the paltry two billion available on a 32-bit computer.

Complex Numbers - Math wizards only. Complex is not as simple as the others. A complex number has two parts, real and imaginary. Conversion to complex typically involves two parameters.

complex(real[, imag]) → number
Creates a complex number with the real part of real; if the second parameter, imag, is given, this is the imaginary part of the complex number, otherwise the imaginary part is zero.

If this syntax synopsis with the [ and ] is confusing, you’ll need to see Function Syntax Rules.

Examples:

```>>> complex(3,2)
(3+2j)
>>> complex(4)
(4+0j)
```

Note that the second parameter, with the imaginary part of the number, is optional. This leads to two different ways to evaluate this function. In the example above, we used both variations.

Conversion from a complex number (effectively two-dimensional) to a one-dimensional integer or float is not directly possible. Typically, you’ll use abs() to get the absolute value of the complex number. This is the geometric distance from the origin to the point in the complex number plane. The math is straight-forward, but beyond the scope of this introduction to Python.

```>>> abs(3+4j)
5.0
```

## Going the Other Way¶

If the int() function turns a string of digits into a proper number, can we do the opposite thing and turn an ordinary number into a string of digits?

The str() and repr() functions convert any Python object to a string. The str() string is typically more readable, where the repr() result can help us see what Python is doing under the hood. For most garden-variety numeric values, there is no difference. For the more complex data types, however, the results of repr() and str() can be different.

Here are some examples of converting floating-point expressions into strings of digits.

```>>> str( 22/7.0 )
'3.14285714286'
>>> repr( 355/113. )
'3.1415929203539825'
```

Note that the results are surrounded by ' marks. These apostrophes tell us that these aren’t actually numbers; they’re strings of digits.

What’s the difference? Try this and see.

```11+12
11+'12'
```

A string of digits may look numeric to you, but Python won’t look inside a string to see if it “looks” like a number. If it is a string (with " or '), it is not a number, and Pyhton won’t attempt to do any math.

Here are the formal definitions of these two functions. These aren’t very useful now, but we’ll return to them time and again as we learn more about how Python works.

str(object) → string
Creates a string representation of object.
repr(object) → string
Creates a string representation of object, usually in Python syntax.

## Most and Least¶

The max() and min() functions accept any number of values and return the largest or smallest of the values. These functions work with any type of data. Be careful when working with strings, because these functions use alphabetical order, which has some surprising consequences.

```>>> max( 10, 11, 2 )
11
>>> min( 'asp', 'adder', 'python' )
>>> max( '10', '11', '2' )
'2'
```

The last example (max( '10', '11', '2' )) shows the “alphabetical order of digits” problem. Superficially, this looks like three numbers (10, 11 and 2). But, they are quoted strings, and might as well be words. What would be result of max( 'ba', 'bb', 'c' ) be? Anything surprising about that? The alphabetic order rules apply when we compare string values. If we want the numeric order rules, we have to supply numbers instead of strings.

Here are the formal definitions for these functions.

max(sequence) → object
Returns the object with the largest value in sequence.
min(sequence) → object
Returns the object with the smallest value in sequence .

## Basic Function Exercises¶

1. Numeric Expressions.

Write an expression to convert the mixed fraction 3 5/8 to a floating-point number.

2. Truncation.

Evaluate (22.0/7.0)-int(22.0/7.0). What is this value? Compare it with 22.0/7.0. What general principal does this illustrate?

3. Illegal Conversions.

Try illegal conversions like int('A'), int(3+4j ), int( 2L**64 ). Why are exceptions raised? Why can’t a simple default value like zero be used instead?