Floating point numbers are represented with a mantissa and exponent: $$\underbrace{0.3423}_{\textrm{mantissa}}\times{}\underbrace{10^{-2}}_{\textrm{exponent}}$$
We are working with computers though, so the mantissa is a binary and the exponent is in powers of 2. Let's look at them as decimals to begin though.
In [1]:
from IPython.display import Math
from math import frexp, pi
import math
#Convert a float into its mantissa and exponent and print as LaTeX
def fprint(x):
m,e = frexp(x)
return Math('{:4} \\times 2^{{{:}}}'.format(m, int(e)))
#Convert a mantissa from decimal to binary and print as LaTeX
def ffrac_ltx(x, terms=10):
bits = []
exp = 1
latex = ''
while x > 0 and exp < terms:
bits.append(int(x / 2.0 ** -exp))
x -= bits[-1] * 2.0 ** -exp
latex += '\\frac{{ {} }}{{ 2^{{ {} }} }} + '.format(bits[-1], exp)
exp += 1
return Math(latex[:-3] + ' = {}'.format(sum([b * 2**-i for b,i in zip(bits,range(1,exp))])))
def ffrac(x, terms=10):
bits = []
exp = 1
while x > 0 and exp < terms:
bits.append(int(x / 2.0 ** -exp))
x -= bits[-1] * 2.0 ** -exp
exp += 1
return ''.join(['{}'.format(x) for x in bits])
In [2]:
fprint(4.3)
Out[2]:
In [3]:
fprint(0.1)
Out[3]:
In [4]:
fprint(pi)
Out[4]:
A 'decimal' in binary is challenging to think about, because each integer location is a $1 / 2^{n}$, where $n$ is the location. That means to represent a binary-mantissa exacly, its denominator must be a power of $2$. For example:
In [5]:
ffrac_ltx(0.5)
Out[5]:
In [6]:
ffrac_ltx(0.75)
Out[6]:
In [7]:
ffrac_ltx(0.5232)
Out[7]:
In [8]:
#This is the maximum number of bits used in the 0.1 Mantissa as a string.
print(ffrac(0.1, 100))
In [9]:
print('{:0.28f}'.format(0.1))
In [10]:
0.1 + 0.2 == (1.0 + 2.0) / 10.0
Out[10]:
In [11]:
print(abs(-43))
In [12]:
x = -5
x = abs(x)
print(x)
In [13]:
from math import fabs
print(fabs(-4.34322))
In [14]:
from math import fabs, sin, cos
print(fabs(sin(4) * cos(43.)))
In [15]:
from math import *
print(e)
In [16]:
print(2 == 5)
In [17]:
a = 3
print(a == 3)
In [18]:
print(a <= 0)
In [19]:
print(a > 0 and a < 4)
Boolean Logic uses Short-Circuiting - only as much as necessary is evaluated.
In [20]:
print(True or 1 / 0.)
In [21]:
print(True and 1 / 0.)
In [22]:
x = 65
if x > 43:
print('Surprise! {} is greater than 43'.format(x))
In [23]:
x = 54
if x < 0:
print('{} is negative'.format(x))
elif x > 0:
print('{} is positive'.format(x))
else:
print('{} is 0'.format(x))
In [24]:
x = -32
if x < 0:
if abs(x) > 5:
print('{} is less than 0 and has magnitude greater than 5'.format(x))
else:
print('{} is not interesting'.format(x))
#or you can do:
if x < 0 and abs(x) > 5:
print('{} is less than 0 and has magnitude greater than 5'.format(x))
if x < 0 and not abs(x) > 5:
print('{} is not interesting'.format(x))
In [25]:
a = 28238
b = 28238
print(a is b, a == b)
In [26]:
a = b
print(a is b)
is None is special case. None is used as a "sentinel" value to indicate if something is not ready or invalid.
In [27]:
var = None
print(var is None)
In [28]:
a = 43
b = 23
print(a != b)
In [29]:
x = 1
if(x):
print('True')
else:
print('False')
In [30]:
x = None
if(x):
print('True')
else:
print('False')
In [31]:
x = 'Hello'
if(x):
print('True')
else:
print('False')
In [32]:
x = ''
if(x):
print('True')
else:
print('False')
In [33]:
x = math.sin(0.0)
if(x):
print('True')
else:
print('False')
It is better to just be explicit and give a full boolean expression. You'll most often see these used by accident
In [34]:
x = 0.15 + 0.15
y = 0.1 + 0.2
print(x == y)
In [35]:
x = 0.15 + 0.15
y = 0.1 + 0.2
abs(x - y) < 10**-8
Out[35]:
In [36]:
numbers = [4,7,24,11,2]
print(numbers)
Individual elements can be accessed with []. The thing that goes inside the brackets is called the index. Recall that python starts counting from 0
In [37]:
print(numbers[0], numbers[1])
In [38]:
print(numbers[0:3])
The last element to the slice operator is not inclusive, so 0:3 means 0, 1, 2. Why? Becuase you expect $3 - 0$ elements, which is three, starting from the $0$ index.
In [39]:
numbers[2:5]
Out[39]:
If it's more convienent, you can count backwards from the end of the list by using negative indices. The trick is that -0 is not really a thing, so the last element in the list is -1.
In [40]:
print(numbers)
print(numbers[-1], numbers[-2])
The index arguments to a slice are optional.
In [41]:
print(numbers[:4])
In [42]:
print(numbers)
print(numbers[:-1])
In [43]:
print(numbers[3:])
In [44]:
print(numbers[-3:])
You can add a third argument by adding another : It is the step-size.
In [45]:
print(numbers)
print(numbers[0:-1:2])
The other slicing indices are optional.
In [46]:
print(numbers[0::2])
In [47]:
print(numbers)
print(numbers[::2])
In [48]:
print(numbers[2::2])
Using a negative stepsize allows you to count downwards
In [49]:
print(numbers[4:0:-1])
Note that we still include the first slice index (4), but not the second one (0).
A common idiom is to reverse the order of list this way:
In [50]:
print(numbers, numbers[::-1])
Notice that Python was clever and knew with a step-size of -1, we actually wanted to start at the far end and count downwards.
In [51]:
x = range(100)
len(x)
Out[51]:
In [52]:
x = range(10)
print(max(x))
In [53]:
import math
math.cos(x)