|$ curl https://forge-ai.dev/api/markdown?path=docs/python/numbers
$cat docs/python-—-numbers-&-math.md
updated Recently·12 min read·published

Python — Numbers & Math

PythonBeginner
Introduction

Python provides three built-in numeric types: int, float, and complex. Together with the math, decimal, fractions, and random modules, you have everything needed for numerical computing — from simple arithmetic to advanced mathematics.

Integers (int)

Python integers are arbitrary-precision (unbounded). They never overflow — they grow as large as memory allows. This is a major difference from languages like C or Java where integers wrap at a fixed size.

integers.py
Python
1# Unbounded precision — no overflow
2tiny = 1
3huge = 10 ** 100 # 1 followed by 100 zeros
4print(huge)
5# → 10000000000000000000000000000000000000000000000000000...
6
7huger = 2 ** 10000 # still works fine
8print(len(str(huger))) # → 3011 digits
9
10# Integer literal bases
11dec = 42 # decimal
12octal = 0o52 # octal → 42
13hexa = 0x2A # hex → 42
14binary = 0b101010 # binary → 42
15
16# Underscores for readability (Python 3.6+)
17big = 1_000_000_000 # same as 1000000000
18hex_mask = 0xFF_FF_FF_FF
19
20# Integer methods
21n = 42
22print(n.bit_length()) # → 6 (bits needed: 101010)
23print(n.to_bytes(2, "big")) # → b'\x00*'
24print(n.to_bytes(2, "little")) # → b'*\x00'
25
26# from_bytes — reconstruct integer
27raw = b'\x00*'
28print(int.from_bytes(raw, "big")) # → 42
29print(int.from_bytes(raw, "little")) # → 13312
30
31# Type conversion
32print(int(3.9)) # → 3 (truncates toward zero)
33print(int("42")) # → 42 (string to int)
34print(int("FF", 16)) # → 255 (parse hex string)
35print(True + 2) # → 3 (bool is int subclass)

info

Because integers are unbounded, you never need to worry about overflow in Python. However, very large integers use more memory and are slower — O(n) for arithmetic where n is the number of digits.
Floats (float)

Floats are IEEE 754 double-precision (64-bit) binary floating-point numbers. They have ~15-17 decimal digits of precision and a range of roughly 5e-324 to 1.8e+308.

floats.py
Python
1# Float precision — the classic gotcha
2print(0.1 + 0.2) # → 0.30000000000000004
3print(0.1 + 0.2 == 0.3) # → False (!)
4
5# Why? 0.1 and 0.2 have no exact binary representation
6# in IEEE 754, just like 1/3 has no exact decimal form
7
8# Workaround: use math.isclose for comparison
9import math
10print(math.isclose(0.1 + 0.2, 0.3)) # → True
11
12# Or round to needed precision
13print(round(0.1 + 0.2, 10) == 0.3) # → True
14
15# Special float values
16inf = float("inf")
17neg_inf = float("-inf")
18nan = float("nan")
19
20print(inf > 1e308) # → True
21print(inf + 1) # → inf
22print(-inf + 1) # → -inf
23print(inf - inf) # → nan
24print(nan == nan) # → False (!) — NaN never equals anything
25print(math.isnan(nan)) # → True — use isnan to check
26
27# Float limits
28import sys
29print(sys.float_info.max) # → 1.7976931348623157e+308
30print(sys.float_info.min) # → 2.2250738585072014e-308
31print(sys.float_info.epsilon) # → 2.220446049250313e-16
32
33# Type conversion
34print(float(42)) # → 42.0
35print(float("3.14")) # → 3.14
36print(float("inf")) # → inf

warning

Never compare floats for exact equality. Use math.isclose or round to a tolerance. For money or decimal-precision work, use the decimal module instead.
Complex Numbers (complex)

Complex numbers have a real and imaginary part, written as a + bj. The imaginary unit is j (not i as in mathematics).

complex.py
Python
1# Creation
2z1 = 3 + 4j # literal form
3z2 = complex(3, 4) # constructor
4print(z1) # → (3+4j)
5
6# Real and imaginary parts
7print(z1.real) # → 3.0
8print(z1.imag) # → 4.0
9
10# Conjugate
11print(z1.conjugate()) # → (3-4j)
12
13# Arithmetic works as expected
14a = 1 + 2j
15b = 3 + 4j
16print(a + b) # → (4+6j)
17print(a * b) # → (-5+10j) (1+2j)*(3+4j) = 3+4j+6j+8j² = -5+10j
18print(a / b) # → (0.44+0.08j)
19
20# Magnitude and phase
21import cmath
22print(abs(a)) # → 2.236... (magnitude: sqrt(1²+2²))
23print(cmath.phase(a)) # → 1.107... (radians)
24print(cmath.polar(a)) # → (2.236, 1.107) (r, theta)
25print(cmath.rect(2.236, 1.107)) # → (1+2j)
26
27# Type conversion — only from other numeric types
28print(complex(5)) # → (5+0j)
29print(complex(5.0)) # → (5+0j)
Arithmetic & Comparison Operators

Python provides a full set of arithmetic and comparison operators. Note that / always returns a float, while // performs floor division.

operators.py
Python
1# Arithmetic operators
2print(10 + 3) # → 13 addition
3print(10 - 3) # → 7 subtraction
4print(10 * 3) # → 30 multiplication
5print(10 / 3) # → 3.3333333333333335 true division (always float)
6print(10 // 3) # → 3 floor division (rounds down)
7print(10 % 3) # → 1 modulo (remainder)
8print(10 ** 3) # → 1000 exponentiation (pow)
9
10# Floor division behavior with negatives
11print(-10 // 3) # → -4 (rounds DOWN, not toward zero)
12print(10 // -3) # → -4
13print(-10 // -3) # → 3
14
15# Modulo with negatives — result has sign of divisor
16print(-10 % 3) # → 2 (because -10 // 3 = -4, -4*3 + 2 = -10)
17print(10 % -3) # → -2
18
19# Exponentiation
20print(2 ** 3) # → 8
21print(2 ** 0.5) # → 1.414... (sqrt via exponent)
22print(2 ** -1) # → 0.5
23print(2 ** 1000) # huge int — no overflow
24
25# Augmented assignment
26x = 10
27x += 3 # x = x + 3 → 13
28x -= 5 # x = x - 5 → 8
29x *= 2 # x = x * 2 → 16
30x /= 4 # x = x / 4 → 4.0 (becomes float)
31x //= 2 # x = x // 2 → 2.0 (still float)
32x = 10
33x %= 3 # x = x % 3 → 1
34x **= 3 # x = x ** 3 → 1
35
36# Comparison operators
37print(5 == 5) # → True (equal)
38print(5 != 5) # → False (not equal)
39print(5 < 10) # → True
40print(5 > 10) # → False
41print(5 <= 5) # → True
42print(5 >= 5) # → True
43
44# Chained comparisons — a Python superpower
45age = 25
46print(18 <= age < 65) # → True (equivalent to: 18 <= age and age < 65)
47print(0 < age < 100) # → True
48
49# Comparison across numeric types
50print(1 == 1.0) # → True (value comparison, not type)
51print(1 == 1 + 0j) # → True
52print(42 < 42.5) # → True (int vs float comparison works)
53
54# Note: complex numbers don't support <, >, <=, >=
55# print(3+4j < 5+6j) # TypeError: '>' not supported

info

Chained comparisons are a Python exclusive. They let you write min <= value <= max instead of the more verbose value >= min and value <= max. They also short-circuit and evaluate each operand only once.
Math Module

The math module provides access to common mathematical functions and constants built on top of the C standard library.

math_module.py
Python
1import math
2
3# Rounding functions
4print(math.ceil(3.1)) # → 4 (round up)
5print(math.ceil(-3.1)) # → -3 (round up toward infinity)
6print(math.floor(3.9)) # → 3 (round down)
7print(math.floor(-3.9)) # → -4 (round down toward -infinity)
8print(math.trunc(3.9)) # → 3 (truncate toward zero)
9print(math.trunc(-3.9)) # → -3 (truncate toward zero)
10print(round(3.5)) # → 4 (built-in, banker's rounding)
11print(round(4.5)) # → 4 (rounds to even — banker's rounding!)
12print(round(3.14159, 2)) # → 3.14 (round to 2 decimal places)
13
14# Power and roots
15print(math.sqrt(16)) # → 4.0
16print(math.pow(2, 10)) # → 1024.0 (always returns float)
17print(2 ** 10) # → 1024 (returns int when possible)
18
19# Logarithms
20print(math.log(100)) # → 4.605... (natural log, base e)
21print(math.log(100, 10)) # → 2.0 (log base 10)
22print(math.log10(100)) # → 2.0 (log base 10, faster)
23print(math.log2(1024)) # → 10.0 (log base 2, faster)
24
25# Trigonometry (angles in radians)
26print(math.sin(math.pi / 2)) # → 1.0
27print(math.cos(0)) # → 1.0
28print(math.tan(math.pi / 4)) # → 0.999...
29print(math.asin(1)) # → pi/2
30print(math.degrees(math.pi)) # → 180.0
31print(math.radians(90)) # → pi/2
32
33# Constants
34print(math.pi) # → 3.141592653589793
35print(math.e) # → 2.718281828459045
36print(math.tau) # → 6.283185307179586 (2 * pi)
37
38# Special values
39print(math.inf) # → inf
40print(math.nan) # → nan
41print(math.isinf(1/0)) # → True
42print(math.isnan(float("nan"))) # → True
43
44# Other useful functions
45print(math.factorial(5)) # → 120
46print(math.comb(5, 2)) # → 10 (combinations: 5 choose 2)
47print(math.perm(5, 2)) # → 20 (permutations: 5 pick 2)
48print(math.gcd(48, 18)) # → 6 (greatest common divisor)
49print(math.lcm(12, 18)) # → 36 (least common multiple, 3.9+)
50print(math.fabs(-3.5)) # → 3.5 (absolute value as float)
51print(math.copysign(5, -3)) # → -5.0 (magnitude of 5, sign of -3)

best practice

Prefer math.log10 over math.log(x, 10) for base-10 logs — it's more accurate and faster. Similarly, math.log2 is best for base-2. For integer exponentiation, use ** instead of math.pow to preserve the int type.
Decimal & Fractions Modules

For exact decimal arithmetic (e.g., financial calculations), use the decimal module. For rational number arithmetic, use fractions.

decimal_fractions.py
Python
1from decimal import Decimal, getcontext, ROUND_HALF_UP
2
3# Decimal from string (never from float!)
4price = Decimal("0.10")
5tax = Decimal("0.20")
6print(price + tax) # → 0.30 (exact!)
7print(0.10 + 0.20) # → 0.30000000000000004 (float — wrong)
8
9# Setting precision and rounding
10getcontext().prec = 28 # default precision (28 decimal places)
11getcontext().rounding = ROUND_HALF_UP # standard rounding
12
13# Decimal arithmetic
14d = Decimal("10.50")
15print(d / 3) # → 3.5 (exact with context precision)
16print(d.quantize(Decimal("0.1"))) # → 10.5 (round to 1 decimal)
17
18# Comparing decimals
19print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3")) # → True
20
21# Fractions — exact rational arithmetic
22from fractions import Fraction
23f1 = Fraction(1, 3) # 1/3
24f2 = Fraction(2, 5) # 2/5
25
26print(f1 + f2) # → 11/15
27print(f1 * f2) # → 2/15
28print(f1 / f2) # → 5/6
29print(float(f1)) # → 0.333...
30print(f1.limit_denominator(10)) # → 1/3 (already simplified)

warning

Never create a Decimal from a float literal: Decimal(0.1) will capture the float's imprecision. Always use strings: Decimal("0.1").
Random Module

The random module implements pseudo-random number generators for various distributions and sampling operations.

random_module.py
Python
1import random
2
3# Basic random floats
4print(random.random()) # → 0.0 <= x < 1.0 (uniform)
5print(random.uniform(5, 10)) # → 5.0 <= x <= 10.0 (uniform float range)
6
7# Integers
8print(random.randint(1, 6)) # → int: 1 <= x <= 6 (inclusive both ends)
9print(random.randrange(10)) # → 0..9
10print(random.randrange(0, 10, 2)) # → 0, 2, 4, 6, 8 (step)
11
12# Sequence operations
13colors = ["red", "green", "blue", "yellow"]
14print(random.choice(colors)) # → single random element
15
16# Shuffle in-place
17random.shuffle(colors) # → colors list is now permuted
18
19# Sample without replacement
20cards = list(range(52))
21hand = random.sample(cards, 5) # → 5 unique cards (no duplicates)
22
23# Sample with replacement (allow duplicates)
24hands = random.choices(cards, k=5) # → 5 cards, possible duplicates
25
26# Weighted choices
27random.choices(
28 ["red", "green", "blue"],
29 weights=[5, 3, 1], # red is 5x more likely than blue
30 k=10
31)
32
33# Seeding for reproducibility
34random.seed(42)
35print(random.random()) # always 0.639...
36random.seed(42)
37print(random.random()) # same value — deterministic sequence

info

Set random.seed() with a fixed value to get reproducible random sequences — essential for testing and debugging. Use secrets module for cryptographic randomness.
Bitwise Operators

Bitwise operators work on the binary representation of integers. They are useful for flags, masks, and low-level protocols.

bitwise.py
Python
1# Bitwise operators — operate on binary bits
2a = 0b1100 # 12 in decimal
3b = 0b1010 # 10 in decimal
4
5print(bin(a)) # → 0b1100 (binary representation)
6
7print(a & b) # AND → 0b1000 (8)
8print(a | b) # OR → 0b1110 (14)
9print(a ^ b) # XOR → 0b0110 (6)
10print(~a) # NOT → -13 (inverts all bits, two's complement)
11print(a << 2) # LSHIFT → 0b110000 (48) — multiply by 2²
12print(a >> 2) # RSHIFT → 0b0011 (3) — floor divide by 2²
13
14# Common use: flags and masks
15READ = 0b001 # 1
16WRITE = 0b010 # 2
17EXEC = 0b100 # 4
18
19permissions = READ | WRITE # → 3 (0b011)
20print(permissions & READ) # → 1 (True — has read)
21print(permissions & EXEC) # → 0 (False — no execute)
22print(permissions ^ READ) # → 2 (toggle read bit)
23
24# Bit_length — useful for binary operations
25n = 255
26print(n.bit_length()) # → 8 (bits: 11111111)
27print(n.bit_count()) # → 8 (popcount, 3.8+)
28
29# Check if power of two
30def is_power_of_two(x):
31 return x > 0 and (x & (x - 1)) == 0
32
33print(is_power_of_two(8)) # → True
34print(is_power_of_two(10)) # → False

info

Left shift by n is equivalent to multiplying by 2**n, and right shift is floor division by 2**n. Bit operations are extremely fast at the hardware level.
Best Practices & Gotchas
best_practices.py
Python
1# 1. Use int for counting, indexing, whole numbers
2count = 1000 # int — fine
3
4# 2. Use Decimal for money
5from decimal import Decimal
6price = Decimal("19.99")
7tax = price * Decimal("0.08")
8total = price + tax # exact decimal result
9
10# 3. Avoid float equality — use tolerance
11import math
12if math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9):
13 print("close enough")
14
15# 4. Use Fraction for exact rationals
16from fractions import Fraction
17ratio = Fraction(22, 7) # exact 22/7
18
19# 5. Use ** or pow for integer exponentiation
20# (math.pow always returns float, losing precision for large ints)
21print(2 ** 100) # int — correct
22print(math.pow(2, 100)) # float — rounded
23
24# 6. Integer division gotcha
25print(5 / 2) # → 2.5 (float — may surprise beginners)
26print(5 // 2) # → 2 (int — floor)
27
28# 7. Large ints are slower
29import time
30n = 10 ** 10000
31# operations on this are O(n) in digit count
32
33# 8. Type conversion awareness
34print(int(3.999)) # → 3 (truncates, not rounds!)
35print(round(3.999)) # → 4 (rounds)
36print(int("0xFF", 16)) # → 255 (parse hex string)
37
38# 9. NaN comparisons
39nan = float("nan")
40print(nan == nan) # → False (always!)
41print(nan is nan) # → True (identity works)
42
43# 10. bool is a subclass of int
44print(True + True) # → 2
45print(False * 10) # → 0
46print(isinstance(True, int)) # → True — but don't abuse this
$Blueprint — Engineering Documentation·Section ID: PYTHON-NUM·Revision: 1.0