Python provides these advanced numeric types in the standard library. The fractions module gives you rational numbers. The decimal module (already covered) gives you exact decimal arithmetic. The complex type is built-in. And integers themselves support advanced bitwise operations.
This lesson explores these advanced numeric capabilities. You will learn to work with fractions for exact rational arithmetic, use complex numbers for scientific computing, perform bitwise operations, and handle large integers efficiently. These skills are essential for scientific programming, cryptography, and low-level systems work.
🕯️ Magic Note
Python integers have unlimited precision. They can grow to the size of your available memory. This makes Python ideal for cryptographic applications, large prime number generation, and any domain requiring big numbers. Contrast this with C, where integers overflow at 2³¹-1 or 2⁶³-1.
Python
from fractions import Fraction
# Creating fractions
f1 = Fraction(1, 3) # 1/3
f2 = Fraction(2, 5) # 2/5
f3 = Fraction(0.75) # 3/4 (from float)
f4 = Fraction(“3/4”) # 3/4 (from string)
print(f1) # 1/3
print(f1 + f2) # 11/15 (1/3 + 2/5 = 5/15 + 6/15)
print(f1 – f2) # -1/15
print(f1 * f2) # 2/15
print(f1 / f2) # 5/6 (1/3 ÷ 2/5 = 5/6)
# Automatic reduction to lowest terms
f = Fraction(4, 8) # Automatically reduced to 1/2
print(f) # 1/2
# Comparison operators work as expected
print(Fraction(1, 3) < Fraction(1, 2)) # True
# Access numerator and denominator
f = Fraction(3, 4)
print(f.numerator) # 3
print(f.denominator) # 4
# Convert to float (may lose precision)
print(float(Fraction(1, 3))) # 0.3333333333333333
🕯️ Magic Note
Fractions are exact. They never have floating point rounding errors. However, they are slower than floats and can cause denominator blowup (very large integers). Use them when exact rational arithmetic is critical, such as in financial calculations or mathematical proofs.
Python
from fractions import Fraction
# Recipe scaling (exact proportions)
original = {“flour”: Fraction(2, 3), “sugar”: Fraction(1, 4), “butter”: Fraction(1, 2)}
scale_factor = Fraction(3, 1) # Triple the recipe
scaled = {ing: qty * scale_factor for ing, qty in original.items()}
print(scaled) # {‘flour’: Fraction(2, 1), ‘sugar’: Fraction(3, 4), ‘butter’: Fraction(3, 2)}
# Currency conversion with exact rates
usd_to_eur = Fraction(92, 100) # 0.92 exchange rate
amount = Fraction(10, 1)
converted = amount * usd_to_eur
print(f”$10 = €{float(converted):.2f}”)
# Music intervals (frequency ratios)
perfect_fifth = Fraction(3, 2)
perfect_fourth = Fraction(4, 3)
octave = Fraction(2, 1)
print(f”Perfect fifth ratio: {perfect_fifth}”)
print(f”Fifth + Fourth = {perfect_fifth * perfect_fourth} (octave)”)
Python
# Creating complex numbers
z1 = 3 + 4j # Real: 3, Imaginary: 4
z2 = complex(3, 4) # Same as above
z3 = 5j # Pure imaginary: 0 + 5j
# Access real and imaginary parts
print(z1.real) # 3.0
print(z1.imag) # 4.0
print(z1.conjugate()) # 3 – 4j
# Arithmetic with complex numbers
a = 2 + 3j
b = 1 – 1j
print(a + b) # (3+2j)
print(a – b) # (1+4j)
print(a * b) # (5+1j) (2*1 + 2*-1 + 3j*1 + 3j*-1 = 2 -2j +3j -3j² = 5 +1j)
print(a / b) # (-0.5+2.5j)
# Magnitude (absolute value)
print(abs(3 + 4j)) # 5.0 (sqrt(3² + 4²))
🕯️ Magic Note
Complex numbers are essential in electrical engineering, quantum mechanics, signal processing, and control systems. Python’s built-in complex type supports all standard mathematical operations, making it a powerful tool for scientific computing.
Python
import cmath, math
z = 1 + 1j
# Phase (angle) in radians
print(cmath.phase(z)) # 0.7853981633974483 (π/4)
# Polar to rectangular conversion
magnitude = 5
angle = cmath.pi / 2 # 90 degrees
z_polar = cmath.rect(magnitude, angle)
print(z_polar) # 5e-17+5j (approximately 0+5j)
# Exponential and logarithmic functions
print(cmath.exp(z)) # e^(1+1j)
print(cmath.log(z)) # Natural log
print(cmath.log10(z)) # Base-10 log
# Trigonometric functions for complex numbers
print(cmath.sin(z))
print(cmath.cos(z))
print(cmath.tan(z))
# Square root (returns both roots, unlike math.sqrt)
print(cmath.sqrt(-1)) # 1j (not raise error)
print(math.sqrt(-1)) # ValueError: math domain error
Python
# Very large integers (no overflow)
big_num = 10 ** 30 # 1 followed by 30 zeros
print(big_num) # 1000000000000000000000000000000
factorial_100 = 1
for i in range(1, 101):
factorial_100 *= i
print(f”100! has {len(str(factorial_100))} digits”) # 158 digits
# Large Fibonacci number
def fib(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
fib_1000 = fib(1000)
print(f”Fibonacci(1000) has {len(str(fib_1000))} digits”) # 209 digits
🕯️ Magic Note
Python integers use a variable-length representation. Small integers (typically -5 to 256) are cached for performance. Larger integers use as many “digits” (in base 2³⁰ or 2¹⁵) as needed. This makes Python ideal for cryptography, primality testing, and exact combinatorial calculations.
Python
# Binary representation
x = 0b1010 # Binary: 10 in decimal
print(bin(x)) # 0b1010
print(bin(42)) # 0b101010
# Bitwise AND (&)
a = 0b1100 # 12
b = 0b1010 # 10
print(bin(a & b)) # 0b1000 (8)
# Bitwise OR (|)
print(bin(a | b)) # 0b1110 (14)
# Bitwise XOR (^) (exclusive OR)
print(bin(a ^ b)) # 0b0110 (6)
# Bitwise NOT (~)
print(bin(~a)) # -0b1101 (-13) (two’s complement representation)
# Left shift (<<)
print(bin(a << 1)) # 0b11000 (24)
print(bin(a << 2)) # 0b110000 (48)
# Right shift (>>)
print(bin(a >> 1)) # 0b110 (6)
print(bin(a >> 2)) # 0b11 (3)
Python
# Define flags as powers of 2
READ = 0b0001 # 1
WRITE = 0b0010 # 2
EXECUTE = 0b0100 # 4
DELETE = 0b1000 # 8
# Combine permissions
permissions = READ | WRITE # 0b0011 (3)
print(f”Binary: {bin(permissions)}”)
# Check if a permission is set
def has_permission(perms, flag):
return (perms & flag) == flag
print(f”Has READ? {has_permission(permissions, READ)}”) # True
print(f”Has EXECUTE? {has_permission(permissions, EXECUTE)}”) # False
# Add a permission
permissions |= EXECUTE # Now has READ | WRITE | EXECUTE
print(f”Has EXECUTE now? {has_permission(permissions, EXECUTE)}”) # True
# Remove a permission
permissions &= ~WRITE
print(f”Has WRITE after removal? {has_permission(permissions, WRITE)}”) # False
# Toggle a permission
permissions ^= READ # If READ set, remove it; if not, add it
🕯️ Magic Note
Bit flags are extremely space-efficient. A single 64-bit integer can represent 64 boolean flags. This pattern is used in system calls, file permissions, window managers, and game engines.
Python
# Binary (prefix 0b or 0B)
a = 0b1010 # 10
b = 0B1111 # 15
c = 0b1111_0000 # 240 (underscores for readability)
# Octal (prefix 0o or 0O)
d = 0o12 # 10
e = 0O17 # 15
# Hexadecimal (prefix 0x or 0X)
f = 0xA # 10
g = 0XFF # 255
h = 0xDEADBEEF # 3735928559
# Conversions
print(bin(42)) # 0b101010
print(oct(42)) # 0o52
print(hex(42)) # 0x2a
# Convert from binary/octal/hex string to integer
print(int(“1010”, 2)) # 10
print(int(“12”, 8)) # 10
print(int(“A”, 16)) # 10
Python
from numbers import Number, Complex, Real, Rational, Integral
# Check numeric types
print(isinstance(42, Integral)) # True
print(isinstance(3.14, Real)) # True
print(isinstance(3+4j, Complex)) # True
print(isinstance(Fraction(1,2), Rational)) # True
print(isinstance(42, Number)) # True
# Type hierarchy: Number -> Complex -> Real -> Rational -> Integral
def describe_number(x):
if isinstance(x, Integral):
return “Integer”
elif isinstance(x, Rational):
return “Fraction”
elif isinstance(x, Real):
return “Float”
elif isinstance(x, Complex):
return “Complex”
else:
return “Unknown”
print(describe_number(42)) # Integer
print(describe_number(Fraction(1,3))) # Fraction
print(describe_number(3.14)) # Float
print(describe_number(3+4j)) # Complex
| Type | Performance | Use Case |
|---|---|---|
| int | Fastest | General counting, indexing, loops |
| float | Fast | Scientific computing, graphics |
| Fraction | Slow (exact rational arithmetic) | Exact fractions, avoiding floating errors |
| Decimal | Moderate (software implementation) | Financial calculations, exact decimals |
| complex | Fast (hardware accelerated) | Electrical engineering, quantum computing |
Python
# Performance comparison (conceptual)
# int and float: operations are C-level (very fast)
# Fraction: operations are Python-level (slower, but exact)
# Use the right type for the right job
- Creating Fraction from float loses exactness (Fraction(0.1) not equal to Fraction(1,10))
- Using complex numbers for purely real calculations (unnecessary overhead)
- Forgetting that integers are immutable (assigning to bits creates new integers)
- Not handling division by zero with Fractions (raises ZeroDivisionError)
- Assuming bitwise operations work on floats (they do not)
- Write a function that adds two fractions using the Fraction class.
- What is the result of 3 + 4j multiplied by its conjugate?
- How do you check if a number is an integer using the numbers module?
- Write a function that uses bitwise operations to check if a number is even.
- Convert the decimal number 42 to binary, octal, and hexadecimal strings.
- Why are fractions slower than floats?
⚡ Whisper
Numbers are not all the same. Integers are exact and fast, growing without bound. Floats are efficient but approximate. Fractions are exact but slower. Decimals are precise for money. Complex numbers open the door to another dimension. Bitwise operations speak the language of computers at the lowest level. Each type has its purpose. Use integers for counting and indexing. Use floats for scientific calculations. Use fractions when exact rational arithmetic matters. Use decimals for money. Use complex numbers for AC circuits and quantum states. Use bitwise for flags and low-level control. The right number type makes your code correct, fast, and clear. Choose wisely. The numbers will obey.