第三屆 Python Deep Dive I - Numeric Type

Introduction

  • 布林值, 0(False), 1(True) | bool
  • 整數(Z), Integer Numbers | int
  • 有理數(Q), Rational Numbers | fraction.Fraction
  • 實數(R), Real Numbers | float/decimal.Decimal
  • 複數(C), Complex Numbers | complex

Integers: Data Types

  • 僅考慮正數,則 8bit 可用來表示 (0 ~ 255),共 256 個整數
  • 若同時考慮正負數,其中一個位元拿去表示正負,而 0 無正負值,可再擠出一個位置 (-128 ~ 127)
  • 16 位元(signed)integers → Range [-32,768 ~ 32,767]
  • 32 位元(signed)integers → Range [-2,147,483,648 ~ 2,147,483,647]
  • 32 位元(unsigned)integers → Range [0 ~ 4,294,967,296]

  • JAVA → Byte(8-bit), short(16-bit), int(32-bit), long(64-bit)
  • Python → The INT object uses a variable number of bits, and increase the bits dynamically (32, 64, 96 bits … etc.)
  • Since INTs are actually objects, there is a further fixed overhead per integer
import sys
print(sys.getsizeof(0))  # an integer object takes at least 24 bytes (Object Overhead)
print(sys.getsizeof(1))  # it takes 24(overhead) + 4 = 28 bytes
print(sys.getsizeof(2**1000))  # it takes 24(overhead) + 136 = 160 bytes
# Python seamlessly dynamically extend the size to accommodate the length of the integer object.
# When the integer gets bigger, it requires more memory for storage and the operation get slower.
28
28
160
import time

def calc(a):
    for i in range(10000000):
        a * 2

start1 = time.perf_counter()
calc(10) # small integer
end1 = time.perf_counter()
print("It takes :", end1 - start1)
print("_" * 50)

start2 = time.perf_counter()
calc(2**10000) # much much bigger integer
end2 = time.perf_counter()
print("It takes :", end2 - start2)
It takes : 0.8512875000014901
__________________________________________________
It takes : 7.090256199706346

Integers: Operations

  • addition(+), subtraction(-), multiplication(*), exponent(**) → return integer
  • However, division(/) --> always return float (even the case without remainder)
  • There are two more operators for integer →
    • // (Floor division - div)
    • % (Modulo - mod)
  • The floor of a real number a → the largest integer <= a (Floor 的定義: 小於等於 實數 a 的最大整數)
    • floor(3.14) → 3
    • floor(1.9999) → 1
    • floor(2) → 2
    • floor(-3.1) → -4 (for negative number, floor is not quite the sams as truncation!)
  • The equation to satisfy => a = b * (a//b) + a % b
    • Example: 155 = 4 * (155 // 4) + (155 % 4) = 4 * 38 + 3
  • Mod → is not the remainder, it’s just something that has to satisfy the equation

print(type(1+1))
print(type(4-10))
print(type(2*3))
print(type(2**5))
print("_" * 50)
print(type(2/3)) # float
print(type(10/2), (10/2))
<class 'int'>
<class 'int'>
<class 'int'>
<class 'int'>
__________________________________________________
<class 'float'>
<class 'float'> 5.0
import math
print(math.floor(3.15))
print(float(3.999999999999999))
print(math.floor(3.999999999999999)) # 小數位數 15 => Still 3

print(float(3.9999999999999999))
print(math.floor(3.9999999999999999)) # 4  => floats have a limited precision in Python

print("_" * 50)
print(math.floor(-3.15)) # -4
print(math.floor(-3.000000000000001)) # -4
print(math.floor(-3.0000000000000001))  # -3 => floats have a limited precision in Python
3
3.999999999999999
3
4.0
4
__________________________________________________
-4
-4
-3
# floor and truncation
import math
a = 33
b = 16
print(a/b)

print(a//b)
print(math.floor(a/b))
print(math.trunc(a/b)) # 2
2.0625
2
2
2
# floor and truncation
import math
a = -33
b = 16
print(a/b)

print(a//b)
print(math.floor(a/b)) # -3
print(math.trunc(a/b)) # -2
-2.0625
-3
-3
-2
# mod --> the equation: a = b* (a // b) + (a % b)
import math
a = 13
b = 4
print(f'{a}/{b} = {a / b}')
print(f'{a}//{b} = {a // b}')
print(f'{a}%{b} = {a % b}')
print('_'*50)
print(a == b * (a//b) + (a%b)) # Equation satisfied
13/4 = 3.25
13//4 = 3
13%4 = 1
__________________________________________________
True
# mod --> the equation: a = b* (a // b) + (a % b)
import math
a = -13
b = 4
print(f'{a}/{b} = {a / b}')
print(f'{a}//{b} = {a // b}')
print(f'{a}%{b} = {a % b}')
print('_'*50)
print(a == b * (a//b) + (a % b))  # Equation satisfied
-13/4 = -3.25
-13//4 = -4
-13%4 = 3
__________________________________________________
True
# mod --> the equation: a = b* (a // b) + (a % b)
import math
a = -13
b = -4
print(f'{a}/{b} = {a / b}')
print(f'{a}//{b} = {a // b}')
print(f'{a}%{b} = {a % b}')
print('_'*50)
print(a == b * (a//b) + (a % b))  # Equation satisfied
-13/-4 = 3.25
-13//-4 = 3
-13%-4 = -1
__________________________________________________
True

Integers: Constructors and Bases - Lecture

  • The int class provides multiple constructors
  • With one numerical parameter
    • a = int(10), b = int(-10) → but we normally use the literal expression instead (a = 10)
    • a = int(10.9), b = int(-10.9) → Result: Truncated (a = 10, b = 10)
    • a = int(True), b = int(False) → (a = 1, b = 0)
  • With strings that can be parsed to a number
    • a = int(“10”) → a = 10
  • With strings that has an optional second parameter: base (2 <= base <= 36) → 0~9, A~Z = 10 + 26 = 36
    • a = int(“1010”, base=2) or a = int(“1010”, 2) → a = 10
    • a = int(“A12F”, base=16) → a = 41263
  • Reverse Process: changing an integer from base 10 to another base
    • bin(10) → ‘0b1010’
    • oct(10) → ‘0o12’
    • hex(10) → ‘0xa’
    • a = 0b1010, b = 0o12, c = 0xA → (a = 10, b = 10, c = 10)

# Base 5

n = 232
b = 5
if b < 2:
    raise ValueError('Base b must be >= 2')
if n < 0:
    raise ValueError('Number n must be >= 0')
if n == 0:
    print("The answer is 0")

digits = []
while n > 0:
    m = n % b
    n = n // b
    digits.insert(0, m)

print(digits)
[1, 4, 1, 2]
# Base 16 with encoding

n = 1485 # number
b = 16 # Base

digits = []
while n > 0:
    m = n % b
    n = n // b
    digits.insert(0, m)

print(digits)

# Start Encoding
map = '0123456789ABCDEF'

# encoding code 1 --> less efficient
encoding1 = ''
for d in digits:
    encoding1 += map[d]
print(encoding1)

# encoding code 2 --> better (Use list comprehension)
encoding2 = ''.join([map[d] for d in digits])
print(encoding2)
[5, 12, 13]
5CD
5CD

Integers: Constructors and Bases - Coding

  • help(int)
print(type(10))

print("_"*50)
print(int()) # default is 0
print(int(10.5))
print(int(10.999999))

print("_"*50)
print(int(True),"-" ,int(False))
<class 'int'>
__________________________________________________
0
10
10
__________________________________________________
1 - 0
import fractions
a = fractions.Fraction(22, 7)
print(a)
print("_"*50)
print(float(a))
22/7
__________________________________________________
3.142857142857143
print(int("12345"))
print("_"*50)
print(int("101", 2)) # 101 in base 2 --> 5 in base 10
print(int("FF", 16)) # FF in base 16 --> 255 in base 10
12345
__________________________________________________
5
255
print(int('A', 11))
print(int('B', 11))
10



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

c:\Users\joe.hu\Desktop\PyCode\DeepDive\Part 1\Section 04 - Numeric Types\S29-35.ipynb Cell 21 line 2
print(int('B', 11))


ValueError: invalid literal for int() with base 11: 'B'
# Base 10 --> Base 2
print(bin(10))
print(bin(5))

print("_"*50)
print(oct(10))  # Base 10 --> Base 8

print("_"*50)
print(hex(255))  # Base 10 --> Base 16
0b1010
0b101
__________________________________________________
0o12
__________________________________________________
0xff
# prefix: 0b --> base 2, 0o --> base 8, 0x --> base 16
print(int(0b101))
print(int(0o12))
print(int(0xff))
5
10
255
def from_base10(n, b):
    if b < 2:
        raise ValueError("Base b must >= 2")
    if n < 0:
        raise ValueError("Number n must be >= 0")
    if n == 0:
        return [0]
    digits = []
    while n > 0:
        m, n = n % b, n // b # use tuple notation, alternatively n, m = divmod(n, b)
        digits.insert(0, m)
    return(digits)

def encode(digits, digit_map):
    if max(digits) >= len(digit_map):
        raise ValueError('digit_map is not long enough')
    else:
        return ''.join([digit_map[d] for d in digits]) # list comprehension

print(from_base10(10, 2))
print(from_base10(255, 16))

print("-"*20 + " Encoded " + "-"*20)
print(encode(from_base10(255, 16),'0123456789ABCDEF'))
[1, 0, 1, 0]
[15, 15]
-------------------- Encoded --------------------
FF
# dealing with negative number
def rebase_from10(number, base):
    digit_map = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    if base < 2 or base > 36:
        raise ValueError("Base must >= 2 or <= 36")
    sign = -1 if number < 0 else 1
    number *= sign

    digits = from_base10(number, base)
    encoding = encode(digits, digit_map)
    if sign == -1:
        encoding = '-' + encoding
    return encoding

print(rebase_from10(10, 2))
print(int(rebase_from10(10, 2), base=2))
print(rebase_from10(-10, 2))
print(int(rebase_from10(-10, 2), base=2))
print('_'*50)
print(rebase_from10(3451, 16))
print(int(rebase_from10(3451, 16), base=16))
print(rebase_from10(-3451, 16))
print(int(rebase_from10(-3451, 16), base=16))
1010
10
-1010
-10
__________________________________________________
D7B
3451
-D7B
-3451

Rational Numbers - Lecture

  • Rational numbers are fractions of integer numbers
  • Any real number with a finite number of digits after the decimal point is also a rational number
  • Example of irrational number => square root of 2, Pi … etc.
  • Can be represented in Python using the Fraction class in the fractions module
  • Constructors
    • Fraction(numerator=0, denominator=1) 未提供參數時,預設分子為 0,分母為 1
    • Fraction(other_fraction)
    • Fraction(float)
    • Fraction(decimal)
    • Fraction(string) => Fraction(‘10’), Fraction(‘0.125’), Fraction(‘22/7’)

Rational Numbers - Coding

from fractions import Fraction
# - help(Fraction)
# Fraction(1) => Fraction(1, 1)
print(Fraction(numerator=2, denominator=1))
print(Fraction(denominator=1, numerator=2))

print('_'*50)
print(Fraction(3, 4))  # three divided by four
print(Fraction(22, 7))
print(Fraction(0.125))  # => Fraction(1, 8)

print('_'*50)
print(Fraction('0.125')) # string can also work
print(Fraction('22/7'))  # string can also work

2
2
__________________________________________________
3/4
22/7
1/8
__________________________________________________
1/8
22/7
from fractions import Fraction

print(Fraction(6, 10)) # Factions are automatically reduced:(自動約分) => Fraction(3, 5)
print(Fraction(1, -4))  # with negative denominator => change to negative numinator Fraction(-1, 4)
x = Fraction(1, -4)
print(x.numerator)
print(x.denominator)

print('_'*50)
x = Fraction(2, 3)
y = Fraction(3, 4)
print(x + y) # 自動擴分 => Fraction(17, 12)
print(x * y) # 最後約分至最簡分數 => Fraction(1, 2)
print(x / y) # => Fraction(8, 9)
3/5
-1/4
-1
4
__________________________________________________
17/12
1/2
8/9
# irrational number - Pi and square root of 2
import math
print(Fraction(math.pi))
print(float(Fraction(math.pi)))  # we can only get a finite representation

print('_'*50)
print(Fraction(math.pi).limit_denominator(10))
print(Fraction(math.pi).limit_denominator(100))
print(Fraction(math.pi).limit_denominator(500))
print(float(Fraction(math.pi).limit_denominator(500)))

print('_'*50)
print(Fraction(math.sqrt(2)))
print(float(Fraction(math.sqrt(2))))  # we can only get a finite representation
884279719003555/281474976710656
3.141592653589793
__________________________________________________
22/7
311/99
355/113
3.1415929203539825
__________________________________________________
6369051672525773/4503599627370496
1.4142135623730951
# not all floating point numbers can be represented precisely or exactly in a computer
print(Fraction(0.125))
print(Fraction(0.3))
print('_'*50)
print(format(0.3, '0.5f'))
print(format(0.3, '0.15f'))
print(format(0.3, '0.25f'))
1/8
5404319552844595/18014398509481984
__________________________________________________
0.30000
0.300000000000000
0.2999999999999999888977698
1個讚