Python/Numbers

Objective

 Learn about Python integers. Learn about non-decimal integers. Learn about Python floats. Learn about precision of floats. Learn about Boolean algebra. (Booleans are a subclass of integers.[1]) Learn about complex numbers in Python. Learn how to convert numbers into different basic data types.

Lesson

Data Types

This is the first of several lessons on the data types used by Python. Computer programs can process instructions that work with many different kinds of data, and the instructions need to be very precise. If you add a word to this sentence, 'add' means something very different from when you add 2 and 3. A computer language has to have a set of rules defining what operations can be applied to different kinds of data, and for this to work, there also has to be a set of rules defining exactly what data can be used with each operation. For example, if you want to calculate grossProfit = salesIncome - costs, a program has to know that these quantities are variables containing numbers rather than just strings of letters. They must have a numerical data type rather than string data type.

If you are not clear about the meaning in computer science of variables and of data types, it may help to brush up on the lesson Introduction_to_Programming/Variables.

Python Integers

Introduction to integers

Python has several data types to represent numbers. This lesson introduces two: integers, and floating point numbers, or 'floats'. We'll discuss floats later in the lesson. An integer, commonly abbreviated to int, is a whole number (positive, negative, or zero). So 7, 0, -11, 2, and 5 are integers. 3.14159, 0.0001, 11.11111, and even 2.0 are not integers, they are floats in Python. To test if this is true, we can use the isinstance built-in function to test if a number is or isn't an integer.

>>> isinstance(7, int)
True
>>> isinstance(0, int)
True
>>> isinstance(-11, int)
True
>>> isinstance(2, int)
True
>>> isinstance(5, int)
True
>>> isinstance(3.14159, int)
False
>>> isinstance(0.0001, int)
False
>>> isinstance(11.11111, int)
False
>>> isinstance(2.0, int)
False


A decimal integer contains one or more digits "0" ... "9". Underscores may be used to improve readability. With one exception described in floats below, leading zeros in a non-zero decimal number are not allowed.

We can perform simple mathematical operations with integers, like addition (+), subtraction (-), multiplication (*), and division (/). Here are some examples using simple math.

>>> 2+2
4
>>> 4-2
2
>>> 6+1
7
>>> 6+7-3
10
>>> 2*2
4
>>> 2*2*2
8
>>> -2
-2
>>> 8/2
4.0
>>> 4*4/2
8.0
>>> 4-4*2
-4
>>> 2-4
-2
>>> 10+10/2
15.0


You should have noticed three things in the above example. First, all mathematical operations follow an order of operations, called precedence; multiplication and division are done first, then addition and subtraction are performed, hence why 10+10/2 didn't result in 10.0. Secondly, when you divide, a float is always the result. Lastly, by putting a minus sign (-) in front of a number, it will become a negative number.

You can do more mathematical operations than the previously demonstrated ones. We can perform a floor division by using two forward slashes (//) to divide and have the result as an integer.

>>> 4 // 2
2
>>> 1 // 8
0
>>> 5 // 5
1
>>> 100 // 5
20
>>> 4 // 3
1


Now, that may save us trouble, but what if we want to get just the remainder of a division? We can perform a modulo operation to get the remainder. To perform a modulo, use a percent sign (%).

>>> 5 % 4
1
>>> 1 % 4
1
>>> 4 % 4
0
>>> 2 % 4
2
>>> 2 % 1
0
>>> 20 % 2
0
>>> 20 % 3
2
>>> -20 % 3
1


The divmod built-in function returns both quotient and remainder:

>>> divmod(7,3)
(2, 1)
>>> (q,r) = divmod(7,3)
>>> q; r
2
1


You can also find the power of a number by using two asterisk symbols (**).

>>> 4 ** 2
16
>>> 4 ** 4
256
>>> 1 ** 11278923689
1
>>> 2 ** 4
16
>>> 10 ** 2
100
>>> 1024 ** 2
1048576
>>> 10 ** 6
1000000
>>> 25 ** (-1/2)
0.2
>>> 4 * - 3 ** 2
-36
>>> 4 * (- 3) ** 2
36
>>> 8 / 4 ** 2
0.5


The operator of exponentiation (**) has higher precedence than * or / or unary -.

If unsure of precedence, you can always use parentheses to force the desired result:

>>> (4 * (- 3)) ** 2
144


There is no limit for the length of integer literals apart from what can be stored in available memory.

Non-decimal Integers

Almost everyone is familiar with ten based numbers. While base 10 is useful for everyday tasks, it isn't ideal for use in the computer world. Three other numeral systems are commonly used in computer science; binary, octal, and hexadecimal. We'll lightly cover Python's use of these in this section. The binary system is essential as all information is represented in binary form in computer hardware. Octal and hexadecimal are convenient for condensing binary numbers to a form that is more easily read by humans, while (unlike decimal) being simple to translate to or from binary. If you have difficulty with this part of the lesson, it may help to brush up on the lesson Numeral_systems in the course Introduction_to_Computers.

Most people have heard of binary and it is often associated with computers. Actually, modern binary made its way into the world far before electricity was widely in use. The binary system is 2 based, which means that only two numbers are used. Of course, these numbers are 0 and 1. So ${\displaystyle 1+1=10_{2},}$ unlike the decimal's ${\displaystyle 1+1=2_{10}.}$ To use binary numbers in python, prepend 0B or 0b to the number.[2]

>>> 0B11
3
>>> 0B1 + 0B1
2
>>> 0B11 + 0B1
4
>>> 0B10001 + 0B1
18
>>> 0B10001 - 0B1
16
>>> bin(2345)
'0b100100101001'
>>> 0b_111_0101_0011
1875


The value returned by bin is a string. The underscore (_) may be used to make numbers more readable.

 Note: In computers, a binary digit of information is called a bit.

The octal numeral system is something that really isn't used anymore, since it was superseded by hexadecimal. The octal system made sense decades ago when hardware was expensive, because the 8 based system can fit into three bits perfectly. Though this scheme fits into bits, it does not fit into a standard byte, which is 8 bits. Since the octal numeral system is 8 based, you can only use numbers "0"..."7". To use octal numbers in python, prepend 0o or 0O to the beginning of the number.[3] You may find it easier to use a lowercase o instead of an uppercase O, since it could be confused as a zero.

>>> 0o3
3
>>> 0o12
10
>>> 0o12 + 0o10
18
>>> 0o12 - 0o03
7
>>> 0o100
64
>>> 0o777
511
>>> 0o777 - 0o111
438
>>> oct(1_234_987)
'0o4554053'
>>> 0o_1234_9876
File "<stdin>", line 1
0o_1234_9876
^
SyntaxError: invalid token
>>> 0o_1234_0765
2736629


The hexadecimal numeral system is widely used when working with computers, because one hexadecimal digit can fit into a nibble (4 bits). Since a standard byte is 8 bits, two nibbles could perfectly fit into a byte, hence why the octal system is rather obsolete. Hexadecimal has 16 digits, which consist of "0"..."9" and "A"..."F" or "a"..."f". "Letters as numbers?", you may say. Indeed, it may be tricky working with letters as numbers, but once you get comfortable with them, it will be easy to use. To use hexadecimal numbers in python, prepend 0x or 0X to the beginning of the number.[4] I suggest using a lowercase x, since it is easier to distinguish from the numbers and uppercase letters.

>>> 0xF
15
>>> 0xF0
240
>>> 0xFF - 0xF
240
>>> 0xF + 0xA
25
>>> 0x2 + 0x2
4
>>> 0x12 - 0xA
8
>>> 0xFF / 0xF
17.0
>>> 0xF * 0xF
225
>>> hex(1_234_987)
'0x12d82b'
>>> 0x_12_D82B
1234987


 Note: You do not have to use just uppercase letters when working with hexadecimal, you can also use lowercase letters if you find it easier.

This topic has been lightly brushed up on and will probably not be used until later in advanced lessons. If you feel a need to learn this, or you want to be proficient at it, the course Introduction to Computers has a lesson called Numeral systems that deals with these numeral systems with a little more in depth teaching.

Bitwise Operators

All integers may be tested or modified by the Bitwise Operators: & (and), | (or), ^ (exclusive or), << (shift left), >> (shift right) and ~ (invert). However it makes good sense to confine our description of these operators to non-decimal integers, particularly binary and hexadecimal.

These operators are called 'bitwise' because they operate on individual bits within the integer.

1. The & operator produces a true output when both corresponding bits are true:

>>> bin (0b1010101 & 0b1111)
'0b101'
>>> bin (0b1010101 & 0b111000)
'0b10000'
>>> hex (0xFF00FF & 0xFF00)
'0x0'


In the first example both input operands

0b1010101
0b   1111
^ ^

have the marked bits set and the result is '0b101'.

2. The | operator produces a true output when at least one of both corresponding bits is true:

>>> bin (0b1010101 | 0b1110)
'0b1011111'
>>> bin (0b1010101 | 0b1100)
'0b1011101'
>>> hex (0xFF00FF | 0x3F0)
'0xff03ff'


In the first example both input operands

0b1010101
0b   1110
^ ^^^^^

have the marked bits set in at least one of the operands and the result is '0b1011111'.

3. The ^ operator produces a true output when exactly one of both corresponding bits is true:

>> bin (0b1010101 ^ 0b1110)
'0b1011011'
>>> bin (0b1010101 ^ 0b1100)
'0b1011001'
>>> hex (0xFF00FF ^ 0x3F0)
'0xff030f'


In the first example both input operands

0b1010101
0b   1110
^ ^^ ^^

have the marked bits set in exactly one of the operands and the result is '0b1011011'.

4. The << operator shifts the operand left by the number of bits specified:

>> bin(0b10101 << 2)
'0b1010100'
>>> bin(0b10101 << 5)
'0b1010100000'
>>> hex(0xFF00FF << 8)
'0xff00ff00'
>>> (0xFF00FF << 8) == (0xFF00FF * 2**8)
True


In the first example the output is the input shifted left 2 bits:

0b  10101
0b1010100
^^

The ouput is the input with two 0's at the right hand end.

5. The >> operator shifts the operand right by the number of bits specified:

>> bin(0b10101 >> 2)
'0b101'
>>> bin(0b10101 >> 5)
'0b0'
>>> hex(0xFF00FF >> 8)
'0xff00'
>>> (0xFF00FF >> 8) == (0xFF00FF // 2**8)
True


In the first example the output is the input shifted right 2 bits:

0b10101
0b  101

The rightmost two bits of the input are lost forever. If you wish to preserve the 2 rightmost bits of the input, before shifting execute:
twoBits = operand & 0x3


The bitwise operators above perform as expected on all integers of (almost) unlimited length:

>>> hex( ( 0x1234_FEDC << 120 ) | ( 0x_CDE_90AB << 60 ) )
'0x1234fedc00000000cde90ab000000000000000'
>>> hex( ( 0x1234_FEDC << 200 ) ^ ( 0x_CDE_90AB << 207 ) )
'0x67d7cab5c00000000000000000000000000000000000000000000000000'


6. The behavior of the invert (~) operator shows that negative numbers are treated as their 2's complement value:

>>> a = 0b1100101100101 ; bin(~a)
'-0b1100101100110'


For a true 1's complement bitwise invert here is one way to do it:

>>> a = 0b1100101100101 ; b = a ^ (  (1 << a.bit_length) - 1  ); bin(b)
'0b11010011010'
>>> c = a + b; bin(c)
'0b1111111111111'   # to test the operation, all bits of c should be set.
>>> (c+1) == ( 1 << (c.bit_length) )
True                # they are.


And another way to do it:

from decimal import *
a = 0b11100100011001110001010111    # a is int
b = bin(a)                          # b is string
print ('a =', b)

formerPrecision = getcontext.prec
getcontext.prec = a.bit_length
d = Decimal.logical_invert( Decimal( b[2:] ) )    # d is string
getcontext.prec = formerPrecision

print ('d =', d)
e = int(str(d),2)                   # e is int
print ('e =', bin(e))

( (a + e) == ( ( 1 << a.bit_length ) - 1 ) ) and print ('successful inversion')


When you execute the above code, you see the following results:

a = 0b11100100011001110001010111
d =      11011100110001110101000
e =    0b11011100110001110101000
successful inversion


The Decimal.logical_invert performs a 1's complement inversion.

Python Floats

Introduction to floats

Although integers are great for many situations, they have a serious limitation, integers are whole numbers. This means that they are not real numbers. A real number is a value that represents a quantity along a continuous line[5], which means that it can have fractions in decimal forms. 4.5, 1.25, and 0.75 are all real numbers. In computer science, real numbers are represented as floats. To test if a number is float, we can use the isinstance built-in function.

>>> isinstance(4.5, float)
True
>>> isinstance(1.25, float)
True
>>> isinstance(0.75, float)
True
>>> isinstance(3.14159, float)
True
>>> isinstance(2.71828, float)
True
>>> isinstance(1.0, float)
True
>>> isinstance(271828, float)
False
>>> isinstance(0, float)
False
>>> isinstance(0.0, float)
True


As a general rule of thumb, floats have a decimal point and integers do not have a decimal point. So even though 4 and 4.0 are the same number, 4 is an integer while 4.0 is a float.

The basic arithmetic operations used for integers will also work for floats. (Bitwise operators will not work with floats.)

>>> 4.0 + 2.0
6.0
>>> -1.0 + 4.5
3.5
>>> 1.75 - 1.5
0.25
>>> 4.13 - 1.1
3.03
>>> 4.5 // 1.0
4.0
>>> 4.5 / 1.0
4.5
>>> 4.5 % 1.0
0.5
>>> 7.75 * 0.25
1.9375
>>> 0.5 * 0.5
0.25
>>> 1.5 ** 2.0
2.25


Some technical information about 'floats.'

A floating point literal can be either pointfloat or exponentfloat.

A pointfloat contains a decimal point (".") and at least one digit ("0"..."9"), for example:

34.45 ; 34. ; .45 ; 0. ; -.00 ; -33. ;

An exponentfloat contains an exponent which ::= ("e" | "E")["+" | "-"]decinteger (decimal integer). These are examples of exponents:

e9 ; e-0 ; e+1 ; E2 ; E-3 ; E+4.

The exponent is interpreted as follows:

${\displaystyle .5e2=.5(10^{2})=50.0;}$ ${\displaystyle -3E1=-3.0(10^{1})=-30.0;}$ ${\displaystyle .003e-5=.003(10^{-5})=3e-08;}$ ${\displaystyle 3e0=3.0(10^{0})=3.0;}$ ${\displaystyle 0090.5e-02=90.5(10^{-2})=0.905;}$ ${\displaystyle 0E0=0.0(10^{0})=0.0.}$

An exponent float can be either:

decinteger exponent, for example: 0e0 ; -3e1 ; 15E-6; or

pointfloat exponent, for example: .5E+2 ; -3.00e-5 ; 123_456.75E-5 ;

Within the floating point literal white space is not permitted. An underscore ("_") may be used to improve readability. Integer and exponent parts are always interpreted using radix 10. Within the context of floating point literals, a "decinteger" may begin with a "0". Numeric literals do not include a sign; a phrase like -1 is actually an expression composed of the unary operator - and the literal 1.

The Precision of Floats

Before you start calculating with floats you should understand that the precision of floats has limits, due to Python and the architecture of a computer. Some examples of errors due to finite precision are displayed below.

>>> 1.13 - 1.1
0.029999999999999805
>>> 0.001 / 11.11
9.000900090009002e-05
>>> 1 + .0000000000000001
1.0
>>> -5.5 % 3.2
0.9000000000000004
>>> float(1_234_567_890_123_456)
1234567890123456.0
>>> float(12_345_678_901_234_567)
1.2345678901234568e+16


In the first example, 1.13 - 1.1 = 0.03, although Python comes to the conclusion that the real answer is 0.029999999999999805. The fact behind this reasoning is based on how the computer stores memory, so the difference lost a little of its precision. As the minuend increases in size, so does its precision. 2.13 - 1.1 = 1.0299999999999998 and 3.13 - 1.1 = 2.03.

In the second example, 0.001 / 11.11 = 9.000900090009002e-05 where e-05 means ten to the power of negative five. The answer could also be 9.000900090009001e-05 depending on how the quotient is rounded, how long the quotient can be stored on the computer, and the most significant number on the right hand side.

In the third example, the sum of the addends 1 + .0000000000000001 = 1.0 although we know that it really is 1 + .0000000000000001 = 1.0000000000000001. The reason the second addend is left out is because of its insignificance. Although this might not matter for every day situations, it may be important for such uses as rocket science and possibly calculus.

The fourth example gives the correct result if rewritten:

>>> ((-5.5*10 ) % (3.2*10)) / 10.0
0.9


When working with Python floats, we need to be aware that there will probably be a margin of error.

Decimal fixed point and floating point arithmetic for extreme precision

The Python "Decimal" module provides support for fast correctly-rounded decimal floating point arithmetic. The module offers several advantages over the float datatype, including:

• Decimal numbers can be represented exactly.
• The decimal module has a user alterable precision (defaulting to 28 places) which can be as large as needed for a given problem.

The usual start to using decimals is importing the module, viewing the current context with getcontext and, if necessary, setting new values for precision, rounding, or enabled traps:

>>> from decimal import *

>>> getcontext
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, FloatOperation, Rounded], traps=[InvalidOperation, DivisionByZero, Overflow])

>>> setcontext(ExtendedContext)
>>> getcontext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[])

>>> setcontext(BasicContext)
>>> getcontext
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])

>>> c = getcontext
>>> c.flags[Inexact] = True
>>> c.flags[FloatOperation] = True
>>> c.flags[Rounded] = True
>>> getcontext
Context(prec=9, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, FloatOperation, Rounded], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])

>>> getcontext.prec = 75    # set desired precision
>>> getcontext
Context(prec=75, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, FloatOperation, Rounded], traps=[Clamped, InvalidOperation, DivisionByZero, Overflow, Underflow])


We are now ready to use the decimal module.

>>> Decimal(3.14)    # Input to decimal is float.
Decimal('3.140000000000000124344978758017532527446746826171875') # Exact value of float 3.14.

>>> Decimal('3.14')    # Input to decimal is string.
Decimal('3.14')        # Exact value of 3.14 in decimal floating point arithmetic.


${\displaystyle ({\sqrt {2}})^{2}}$

>>> (2 ** 0.5)**2
2.0000000000000004     # Result of binary floating point operation. We expect 2.

>>> (Decimal('2') ** Decimal('0.5')) ** Decimal('2')
Decimal('1.99999999999999999999999999999999999999999999999999999999999999999999999999')
# Result of decimal floating point operation with string input. We expect 2.


${\displaystyle (2.12345678^{({\frac {1}{2.345}})})^{2.345}}$

>>> (2.12345678 ** (1/2.345)) ** 2.345
2.1234567800000006      # Result of floating point operation. We expect 2.12345678.

>>> (Decimal('2.12345678') ** (Decimal('1')/Decimal('2.345'))) ** Decimal('2.345')
Decimal('2.12345677999999999999999999999999999999999999999999999999999999999999999999')
# Result of decimal floating point operation with string input . We expect 2.12345678.

>>> getcontext.rounding=ROUND_UP
>>> (Decimal('2.12345678') ** (Decimal('1')/Decimal('2.345'))) ** Decimal('2.345')
Decimal('2.12345678000000000000000000000000000000000000000000000000000000000000000003')
# Result of decimal floating point operation with string input . We expect 2.12345678.


Some mathematical functions are also available to Decimal:

>>> getcontext.prec = 30

>>> Decimal(2).sqrt
Decimal('1.41421356237309504880168872421')

>>> (Decimal(2).sqrt)**2
Decimal('2.00000000000000000000000000001')  # We expect 2.

>>> Decimal(1).exp
Decimal('2.71828182845904523536028747135')   # Value of 'e', base of natural logs.

>>> Decimal(  Decimal(1).exp  ).ln
Decimal('0.999999999999999999999999999999')   # We expect 1.


Lack of precision in the real world

(included for philosophical interest)

>>> a = 899_999_999_999_999.1 ; a - (a - .1)
0.125
>>> 1.13 - 1.1
0.029999999999999805


Simple tests indicate that the error inherent in floating point operations is about ${\displaystyle {\frac {1}{10^{16}}}.}$

This raises the question "How much precision do we need?"

For decades high school students calculated sines and cosines to 4 decimal places by referring to printed look-up tables. Before computers engineers used slide rules to make calculations accurate to about ${\displaystyle {\frac {1}{1000}}}$ for most calculations, and the Brooklyn Bridge is still in regular use.

With accuracy of ${\displaystyle {\frac {1}{10^{16}}}}$ engineers can send a rocket to Pluto and miss by 1cm.

If your calculations produce a result of ${\displaystyle 1.0(10^{-14})}$ and you were expecting ${\displaystyle 0,}$ will you be satisfied with your work? If your calculations were in meters, probably yes. If your calculations were in nanometers (${\displaystyle 10^{-9}}$ of a meter), probably no.

Knowing that lack of precision is inherent in floating point operations, you may have to include possibly substantial amounts of code to make allowances for it.

Extreme Precision

(included for historical interest)

If you must have an answer correct to 30 places of decimals, Python's integer math comes to the rescue. Suppose your calculation is:

${\displaystyle 123456.789/4567.87654}$ ${\displaystyle ={\frac {123456.789}{4567.87654}}}$ ${\displaystyle ={\frac {123456.78900}{4567.87654}}}$ ${\displaystyle ={\frac {12345678900}{456787654}}}$

and you want 30 significant digits after the decimal point. Your calculation becomes:

${\displaystyle 123456.789/4567.87654}$ ${\displaystyle ={\frac {12345678900(10^{31})}{456787654(10^{31})}}}$ ${\displaystyle ={\frac {12345678900(10^{31})}{456787654}}/10^{31}}$

Begin with:

${\displaystyle 12345678900\_000\_000\_000\_000\_000\_000\_000\_000\_000\_000\_0\ //\ 456787654}$ ${\displaystyle =270271728928995966252625558045401.}$

The correct result ${\displaystyle =270271728928995966252625558045401(10^{-31}).}$

Put the decimal point in the correct position:

${\displaystyle =27.027\_172\_892\_899\_596\_625\_262\_555\_804\_540\_1.}$

Drop the last digit at the right hand end and use it to round up/down as appropriate and the result is:

${\displaystyle =27.027\_172\_892\_899\_596\_625\_262\_555\_804\_540.}$

The Boolean

In Python and most languages, a Boolean can be either True or False. A Boolean is a special data type and is a subclass of int.[6] Since a Boolean has two states and only one at a time, a Boolean creates a special relationship between things. We can think of some Boolean values that we deal with in real life, for example: on or off, hot or cold, light or darkness, etc. Although a Boolean can be True or False a Boolean expression can take a statement, like 1 == 1 or 1 == 0, and turn it into a Boolean, True for the former and False for the latter. We can use the bool method to check the Boolean value of an object, which will be False for integer zero and for objects (numerical and other data types) that are empty, and True for anything else.

>>> 1 == 1
True
>>> 1 == 0
False
>>> bool(0)
False
>>> bool(1)
True
>>> bool(10001219830)
True
>>> bool(-1908)
True
>>> bool("Hello!")
True
>>> bool("")
False
>>> bool("              ")
True
>>> bool(None)
False
>>> bool(0.000000000000000000000000000000000)
False
>>> bool("0.000000000000000000000000000000000")
True
>>> bool(0.0)
False
>>> bool([])
False
>>> bool([1, 2, 3])
True
>>> bool
False
>>> bool(True)
True
>>> bool(False)
False
>>> bool(1==1)
True
>>> bool(1==0)
False


 Note: True and False are both case-sensitive, which means that you must type them exactly as shown, otherwise you'll get a syntax error.

You can also use three operators to alter a Boolean statement[7]: or, and, not. You can use an or statement to allow one or more Booleans to be False so long as one is True. An and statement requires all of the Booleans to be True for it be True. The not statement reverses a Boolean so not True is False and not False is True. Here are some examples:

>>> not False
True
>>> not True
False
>>> True and True
True
>>> True and False
False
>>> True or False
True
>>> False or False
False
>>> not(False or False)
True
>>> not(False and False)
True
>>> not(False and True)
True


Complex Numbers

A complex number is represented as a+bi where a and b are real numbers, like 7 or 12, and i is an imaginary number, where i² = -1. In the computer field, and in the world of Python, i is denoted as j for technical reasons, so we use a+bj. It should also be noted that a and b are both treated as floats. This subject will be briefly covered until later lessons.

>>> 1+2j
(1+2j)
>>> -1+5.5j
(-1+5.5j)
>>> 0+5.5j
5.5j
>>> 2j
2j
>>> 1+0j
(1+0j)
>>> complex(3,-2)
(3-2j)


Note also that j cannot be used on its own without b. If you try to use j on its own, Python will look for a variable j and use the value of that variable, or report an error if the variable is not known or a wrong type. So the imaginary number i or j must always be written as 1j.

>>> a = 5 + 3j
>>> a - j
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'j' is not defined
>>> a - 1j
(5+2j)
>>> j = -3j
>>> a - j
(5+6j)
>>> a - 1j
(5+2j)


The last result illustrates that even when the variable j has a numerical value, 1j (where, as above, can be any number) is always interpreted as the imaginary number j, not the variable j.

The usual mathematical operations can be performed on complex numbers:

>>> (1+3j)+(2-5j)
(3-2j)
>>> (1+3j)-(2-5j)
(-1+8j)
>>> (1+3j)*(2-5j)
(17+1j)
>>> a = complex(3,-5) ; b = 1 ; b += 2j ; a ; b
(3-5j)
(1+2j)
>>> a + b ; a - b
(4-3j)
(2-7j)
>>> a * b ; a / b
(13+1j)
(-1.4-2.2j)
>>> a + 4 ; b - 2j ; a * 3.1 ; b / 2
(7-5j)
(1+0j)
(9.3-15.5j)
(0.5+1j)
>>> b ; b /= 5 ; b
(1+2j)
(0.2+0.4j)
>>> a = complex(3,-5j) ; a
(8-0j)


Look closely at the last example. It does not produce an error, but is it what you want?

 Note: the imaginary number, j, isn't case-sensitive, so you can use j or J.

You can extract the real number and the imaginary number by using .real and .imag respectively.

>>> (1+2j).real
1.0
>>> (1+2j).imag
2.0
>>> var = 5+3j
>>> var.real
5.0
>>> var.imag
3.0


 Note:You'll get weird results if you don't use parentheses on a real number that isn't stored in a variable.

Number Conversions

Introduction

Since integers and floats can't be mixed together in some situations, you'll need to be able to convert them from one type to another. Luckily, it's very easy to perform a conversion. To convert a data type to an integer, use the int function.

>>> int(1.5)
1
>>> int(10.0)
10
>>> int(True)
1
>>> int(False)
0
>>> int('0xFF', base=16) ; int('0xF1F0', 16) ; int('0b110100111', 0) ; int('11100100011',2)
255
61936
423
1827


You can even convert strings, which you'll learn about later.

>>> int("100")
100


To convert a data type to a float, use the float function. Like the integer, you can convert strings to floats.

>>> float(102)
102.0
>>> float(932)
932.0
>>> float(True)
1.0
>>> float(False)
0.0
>>> float("101.42")
101.42
>>> float("4")
4.0


 Note: You cannot use any of the above conversions on a complex number, as it will raise an error. You can work around this by using .real and .imag

You can also use the bool function to convert a data type to a Boolean.

>>> bool(1)
True
>>> bool(0)
False
>>> bool(0.0)
False
>>> bool(0.01)
True
>>> bool(14)
True
>>> bool(14+3j)
True
>>> bool(3j)
True
>>> bool(0j)
False
>>> bool("")
False
>>> bool("Hello")
True
>>> bool("True")
True
>>> bool("False")
True


 Note: Notice that bool("False") is True. Unlike int and float, when bool converts a string, it checks to see if the string is empty or not.

Converting a data type to a complex is a little more tricky, but still easy. All you need to do is use the function complex which takes two parameters, one of which is optional. The first parameter is the real number, which is required, and the second parameter is the imaginary number, which is optional.

>>> complex(True)
(1+0j)
>>> complex(False)
0j
>>> complex(3, 1)
(3+1j)
>>> complex(1, 22/7)
(1+3.142857142857143j)
>>> complex(0, 1.5)
1.5j
>>> complex(7, 8)
(7+8j)
>>> complex("1")
(1+0j)
>>> complex("1+4j")
(1+4j)
>>> complex("9.75j")
9.75j


Converting integers, decimal to non-decimal

This conversion is from int to str representing int:

>>> a = 12345678901234567890
>>> b = bin(a) ; b
'0b1010101101010100101010011000110011101011000111110000101011010010'
>>> h = hex(a) ; h
>>> o = oct(a) ; o
'0o1255245230635307605322'
>>>


Converting integers, non-decimal to decimal

This conversion is from str representing int to int:

>>> a;b;h;o
12345678901234567890
'0b1010101101010100101010011000110011101011000111110000101011010010'
'0o1255245230635307605322'
>>>
>>> int(b,base=0) == int(b,base=2) == int(b,0) == int(b,2) == a # Base 0 or correct base is required.
True
>>> int(h,16) == a
True
>>> int(o,8) == a
True
>>>
>>> int ('ab54a98ceb1f0ad2', 16) == a # When base 16 is supplied, the prefix '0x' is not necessary.
True
>>>
>>> eval(b) == a # Function eval(...) provides simple conversion from str to base type.
True
>>> eval(h) == a
True
>>> eval(o) == a
True
>>>
>>> int('12345678901234567890',0) == int('12345678901234567890',base=0) == a
True
>>> int('12345678901234567890',10) == int('12345678901234567890',base=10) == a
True
>>> eval('12345678901234567890') == int('12345678901234567890') == a
True
>>>


Converting int to bytes

Method int.to_bytes(length, byteorder, *, signed=False) returns a bytes object representing an integer where:

    length (in bytes) must be sufficient to contain int, at least (int.bit_length + 7) // 8
byteorder can be 'big', 'little' or sys.byteorder,
signed must be True if int is negative.


For example:

>>> int1 = 0x1205
>>> bytes1 = int1.to_bytes(2, byteorder='big') ; bytes1
b'\x12\x05' # A bytes object containing int1.
>>> isinstance(bytes1, bytes)
True
>>>
>>> int2 = 0xe205
>>> bytes2 = int2.to_bytes(2, byteorder='big', signed=True) ; bytes2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>>
>>> bytes2 = int2.to_bytes(3, byteorder='big', signed=True) ; bytes2
b'\x00\xe2\x05'
>>>
>>> bytes2 = int2.to_bytes(2, byteorder='big') ; bytes2
b'\xe2\x05'
>>>
>>> int3 = -7675
>>> bytes3 = int3.to_bytes(2, byteorder='big') ; bytes3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: can't convert negative int to unsigned
>>>
>>> bytes3 = int3.to_bytes(2, byteorder='big', signed=True) ; bytes3
b'\xe2\x05'
>>>
>>> bytes2 == bytes3
True
>>>


The bytes object b'\xe2\x05' can represent 0xe205 or -7675 depending on whether it's interpreted as signed or unsigned.

To preserve the original int, let length = ((int.bit_length + 7) // 8) + 1 if necessary and use signed=True.

>>> hex(int2); hex(int3)
'0xe205'
'-0x1dfb'
>>> bytes2 = int2.to_bytes(3, byteorder='big', signed=True) ; bytes2
b'\x00\xe2\x05' # Most significant bit (value=0) preserves sign (+).
>>> bytes3 = int3.to_bytes(2, byteorder='big', signed=True) ; bytes3
b'\xe2\x05' # Most significant bit (value=1) preserves sign (-).
>>>


Converting bytes to int

A bytes object is an immutable sequence with every member ${\displaystyle x}$ an int satisfying 0xFF ${\displaystyle >=x>=0.}$

The classmethod int.from_bytes(bytes, byteorder, *, signed=False) may be used to convert from bytes object to int.

The value returned is an int represented by the given bytes object or any sequence convertible to bytes object:

>>> hex(int.from_bytes(b'\xcd\x34', byteorder='little'))
'0x34cd'
>>> hex(int.from_bytes(b'\xcd\x34', byteorder='big'))
'0xcd34'
>>> hex(int.from_bytes(b'\xcd\x34', byteorder='little', signed=True))
'0x34cd'
>>> hex(int.from_bytes(b'\xcd\x34', byteorder='big', signed=True))
'-0x32cc'
>>> hex(int.from_bytes([0xCD,0x34], byteorder='big')) # Input is list convertible to bytes.
'0xcd34'
>>> hex(int.from_bytes((0xCD,0x34), byteorder='big')) # Input is tuple convertible to bytes.
'0xcd34'
>>> hex(int.from_bytes({0xCD,0x34}, byteorder='big'))
'0x34cd' # Ordering of set is unpredictable.
>>> hex(int.from_bytes(bytes([0xCD,0x34]), byteorder='big')) # Input is bytes object.
'0xcd34'
>>> hex(int.from_bytes(bytearray([0xCD,0x34]), byteorder='big')) # Input is bytearray.
'0xcd34'
>>>


Complete conversion

Complete conversion means conversion from int to bytes to int, or from bytes to int to bytes. When converting int/bytes/int, it is reasonable to expect that the final int should equal the original int. If you keep byteorder consistent and signed=True, you will produce consistent results:

Positive number with msb (most significant bit) clear:

>>> int1 = 0x1205
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
b'\x12\x05'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'0x1205'
>>> int1==int1a
True


Negative number with msb clear:

>>> int1 = -0x1205
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
b'\xed\xfb'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'-0x1205'
>>> int1==int1a
True


Positive number with msb set:

>>> int1 = 0xF205
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> bytes1 = int1.to_bytes(3, byteorder='big', signed=True) ; bytes1
b'\x00\xf2\x05'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'0xf205'
>>> int1==int1a
True


Negative number with msb set:

>>> int1 = -0xF305
>>> bytes1 = int1.to_bytes(2, byteorder='big', signed=True) ; bytes1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> bytes1 = int1.to_bytes(3, byteorder='big', signed=True) ; bytes1
b'\xff\x0c\xfb'
>>> int1a = int.from_bytes(bytes1, byteorder='big', signed=True) ; hex(int1a)
'-0xf305'
>>> int1==int1a
True


Assignments

 Experiment with the python interpreter using integers, floats, Booleans, and complex numbers. Think critically about integers and floats. When should you use integers? When should you use floats? A loss of significance for a float value should be expected when working with long or insignificant numbers. When would this become a problem?

References

8. Python's built-in functions:

9. Python's documentation:

This article uses material from the Wikipedia page available here. It is released under the Creative Commons Attribution-Share-Alike License 3.0.

Manage research, learning and skills at IT1me. Create an account using LinkedIn to manage and organize your IT knowledge. IT1me works like a shopping cart for information -- helping you to save, discuss and share.