Numeric types

At some point, you will have to work with numbers, so we start by considering different forms of numeric types in Python. In mathematics, we distinguish between natural numbers (ℕ), integers (ℤ), rational numbers (ℚ), real numbers (ℝ) and complex numbers (ℂ). These are infinite sets of numbers. Operations differ between these sets and may even not be defined. For example, the usual division of two numbers in ℤ might not result in an integer — it is not defined on ℤ.

In Python, like many other computer languages, we have numeric types:

  • The numeric type int, which is at least theoretically the entire ℤ
  • The numeric type float, which is a finite subset of ℝ and
  • The numeric type complex, which is a finite subset of ℂ

Finite sets have a smallest and a largest number and there is a minimum spacing between two numbers; refer to the section on Floating Point Representation for further details.

Integers

The simplest numerical type is the integer type.

Plain integers

The statement k = 3 assigns the variable k to an integer.

Applying an operation of the type +-, or * to integers returns an integer. The division operator, //, returns an integer, while / may return a float:

6 // 2  # 3
7 // 2  # 3
7 / 2   # 3.5

The set of integers in Python is unbounded; there is no largest integer. The limitation here is the computer’s memory rather than any fixed value given by the language.

Tip

If the division operator (/) in the example returns 3, you might not have installed the correct Python version.

Floating point numbers

If you execute the statement a = 3.0 in Python, you create a floating-point number (Python type: float). These numbers form a subset of rational numbers, ℚ.

Alternatively the constant could have been given in exponent notation as a = 30.0e-1 or simply a = 30.e-1. The symbol e separates the exponent from the mantissa, and the expression reads in mathematical notation a = 30.0 × 10−1. The name floating-point number refers to the internal representation of these numbers and reflects the floating position of the decimal point when considering numbers over a wide range.

Applying the elementary mathematical operations +-*, and / to two floating-point numbers or to an integer and a floating-point number returns a floating-point number. Operations between floating-point numbers rarely return the exact result expected from rational number operations:

0.4 - 0.3 # returns 0.10000000000000003

This facts matters, when comparing floating point numbers:

0.4 - 0.3 == 0.1 # returns False

Floating point representation

Internally, floating-point numbers are represented by four quantities: the sign, the mantissa, the exponent sign, and the exponent:

Floating point representation

with β ϵ and x0≠ 0, 0 ≤ xi≤ β

x0...xt-1 is called the mantissa, β the basis and e the exponent |e| ≤ U . t is called the mantissa length. The condition x0 ≠ 0 makes the representation unique and saves, in the binary case (β = 2), one bit.

There exist two-floating point zeros +0 and -0, both represented by the mantissa 0.

On a typical Intel processor, β = 2 . To represent a number in the float type 64 bits are used, namely 2 bits for the signs, t = 52 bits for the mantissa and 10 bits for the exponent |e|. The upper bound U for the exponent is consequently 210-1 = 1023.

With this data the smallest positive representable number is

flmin = 1.0 × 2-1023 ≈ 10-308  and the largest is  flmax = 1.111...1 × 21023 ≈ 10308.

Note that floating-point numbers are not equally spaced in [0, flmax]. There is in particular a  gap at zero (refer to [29]). The distance between 0 and the first positive number is 2-1023, while the distance between the first and the second is smaller by a factor 2-52≈ 2.2 × 10-16. This effect, caused by the normalization x0 ≠ 0, is visualized in Figure 2.1.

This gap is filled equidistantly with subnormal floating-point numbers to which such a result is rounded. Subnormal floating-point numbers have the smallest possible exponent and do not follow the convention that the leading digit x0 has to differ from zero; refer to [13].

Infinite and not a number

There are in totalInfinite and not a number floating-point numbers. Sometimes a numerical algorithm computes floating-point numbers outside this range.

This generates number over- or underflow. In SciPy the special floating-point number inf is assigned to overflow results:

exp(1000.) # inf 
a = inf
3 - a   # -inf
3 + a   # inf

Working with inf may lead to mathematically undefined results. This is indicated in Python by assigning the result another special floating-point number, nan. This stands for not-a-number, that is, an undefined result of a mathematical operation:

a + a # inf
a - a # nan 
a / a # nan

There are special rules for operations with nan and inf. For instance, nan compared to anything (even to itself) always returns False:

x = nan 
x < 0 # False
x > 0 # False
x == x # False

See Exercise 4 for some surprising consequences of the fact that nan is never equal to itself.

The float inf behaves much more as expected:

0 < inf     # True 
inf <= inf  # True 
inf == inf  # True 
-inf < inf  # True 
inf - inf   # nan 
exp(-inf)   # 0 
exp(1 / inf)  # 1

One way to check for nan and inf is to use the  isnan and isinf functions. Often, one wants to react directly when a variable gets the value nan or inf. This can be achieved by using the NumPy command seterr. The following command

seterr(all = 'raise')

would raise an error if a calculation were to return one of those values.

Underflow - Machine Epsilon

Underflow occurs when an operation results in a rational number that falls into the gap at zero; refer to Figure 2.1.

Underflow - Machine Epsilon

Figure 2.1: The floating point gap at zero, here t = 3, U = 1

The machine epsilon or rounding unit is the largest number ε such that float(1.0 + ε) = 1.0.

Note that ε β1-t/2 = 1.1102 × 10-16 on most of today’s computers. The value that is valid on the actual machine you are running your code on is accessible using the following command:

import sys 
sys.float_info.epsilon # 2.220446049250313e-16 (something like that)

The variable sys.float_info  contains more information about the internal representation of the float type on your machine.

The function float  converts other types to a floating-point number—if possible. This function is especially useful when converting an appropriate string to a number:

a = float('1.356')

Other float types in NumPy

NumPy also provides other float types, known from other programming languages as double-precision and single-precision numbers, namely float64 and float32:

a = pi            # returns 3.141592653589793 
a1 = float64(a)   # returns 3.1415926535897931 
a2 = float32(a)   # returns 3.1415927 
a - a1            # returns 0.0 
a - a2            # returns -8.7422780126189537e-08

The second last line demonstrates that a and a1 do not differ in accuracy. In the first two lines, they only differ in the way they are displayed. The real difference in accuracy is between a and its single-precision counterpart, a2.

The NumPy function finfo  can be used to display information on these floating-point types:

f32 = finfo(float32) 
f32.precision   # 6 (decimal digits) 
f64 = finfo(float64) 
f64.precision   # 15 (decimal digits) 
f = finfo(float) 
f.precision     # 15 (decimal digits) 
f64.max         # 1.7976931348623157e+308 (largest number) 
f32.max         # 3.4028235e+38 (largest number) 
help(finfo)     # Check for more options

Complex numbers

Complex numbers are an extension of the real numbers frequently used in many scientific and engineering fields.

Complex Numbers in Mathematics

Complex numbers consist of two floating-point numbers, the real part a of the number and its imaginary part b. In mathematics, a complex number is written as z=a+bi, where i defined by i2 = -1 is the imaginary unit. The conjugate complex counterpart of z is Complex Numbers in Mathematics.

If the real part a is zero, the number is called an imaginary number.

The j notation

In Python, imaginary numbers are characterized by suffixing a floating-point number with the letter j, for example, z = 5.2j. A complex number is formed by the sum of a floating-point number and an imaginary number, for example, z = 3.5 + 5.2j.

While in mathematics the imaginary part is expressed as a product of a real number b with the imaginary unit i, the Python way of expressing an imaginary number is not a product: j is just a suffix to indicate that the number is imaginary.

This is demonstrated by the following small experiment:

b = 5.2 
z = bj   # returns a NameError 
z = b*j  # returns a NameError
z = b*1j # is correct

The method conjugate returns the conjugate of z:

z = 3.2 + 5.2j 
z.conjugate() # returns (3.2-5.2j)

Real and imaginary parts

One may access the real and imaginary parts of a complex number z using the real and imag attributes. Those attributes are read-only:

z = 1j 
z.real       # 0.0 
z.imag       # 1.0 
z.imag = 2   # AttributeError: readonly attribute

It is not possible to convert a complex number to a real number:

z = 1 + 0j 
z == 1     # True 
float(z)   # TypeError

Interestingly, the real and imag attributes as well as the conjugate method work just as well for complex arrays (Chapter 4Linear Algebra – Arrays). We demonstrate this by computing the Nth roots of unity which are Real and imaginary parts, that is, the N solutions of the equation Real and imaginary parts:

N = 10
# the following vector contains the Nth roots of unity: 
unity_roots = array([exp(1j*2*pi*k/N) for k in range(N)])
# access all the real or imaginary parts with real or imag:
axes(aspect='equal')
plot(unity_roots.real, unity_roots.imag, 'o')
allclose(unity_roots**N, 1) # True

The resulting figure (Figure 2.2) shows the roots of unity together with the unit circle. (For more details on how to make plots, refer Chapter 6, Plotting.)

Real and imaginary parts

Figure 2.2: Roots of unity together with a unit circle

It is of course possible to mix the previous methods, as illustrated by the following examples:

z = 3.2+5.2j 
(z + z.conjugate()) / 2.   # returns (3.2+0j) 
((z + z.conjugate()) / 2.).real   # returns 3.2 
(z - z.conjugate()) / 2.   # returns 5.2j 
((z - z.conjugate()) / 2.).imag   # returns 5.2 
sqrt(z * z.conjugate())   # returns (6.1057350089894991+0j)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.123.189