PythonMastery
beginner 14 min read · lesson 3 of 19 in Python Fundamentals

Numbers in Depth: Integers, Floats, and the math Module

1 · The lesson

read

You met int and float in the Variables lesson. This lesson goes deeper: the three numeric types, integer division, the floating-point rounding problem that every developer meets, formatting numbers for display, and the most useful pieces of the math and random modules.


1. The Three Numeric Types

python
a = 42                          # int: a whole number of any size
b = 3.14                        # float: a decimal number, about 15-17 significant digits
c = 2 + 3j                      # complex: a real part plus an imaginary part

print(type(a), type(b), type(c))

You'll use int and float constantly. Complex numbers are used in engineering and scientific computing. You can ignore them until you need them.


2. Integers Have No Maximum Size

In many other languages, such as C and Java, the usual integer type has a fixed size and cannot hold 2**63 or larger (it overflows). Python integers can grow as large as your computer's memory allows.

python
big = 2 ** 100
print(big)                      # 1267650600228229401496703205376

# Even bigger
factorial_50 = 1
for i in range(1, 51):
    factorial_50 *= i
print(factorial_50)             # 50! (50 factorial) has 65 digits, and Python handles it easily

Underscores make long literals readable (the underscores are ignored):

python
million = 1_000_000             # easier to scan than 1000000
print(million == 1000000)       # True

3. The Floating-Point Trap

0.1 + 0.2 does not equal 0.3 in any language that uses binary floating-point.

python
print(0.1 + 0.2)                # 0.30000000000000004
print(0.1 + 0.2 == 0.3)         # False  (this is correct behaviour, not a bug)

This is not a bug in Python. It comes from the way computers store decimal numbers in binary. The number 0.1 can't be stored exactly in binary, the same way 1/3 can't be stored exactly in base 10.

The fix: use math.isclose() to compare floats safely.

python
import math

print(math.isclose(0.1 + 0.2, 0.3))         # True

# For money or anything needing exact decimals, use the `decimal` module:
from decimal import Decimal
result = Decimal("0.1") + Decimal("0.2")
print(result)                                # 0.3  (exactly 0.3)

Rule of thumb: never compare floats with ==. Always use math.isclose() or work in fixed precision with Decimal.


4. Built-in Number Functions

You don't need to import anything to use these. They are built into the language.

python
print(abs(-7.5))                # 7.5     (the absolute value: distance from zero)
print(round(3.14159, 2))        # 3.14    (rounded to 2 decimal places)
print(round(2.5))               # 2       because Python rounds a .5 to the nearest even number
print(pow(2, 10))               # 1024    (the same as 2 ** 10)
print(divmod(17, 5))            # (3, 2)  (the quotient and the remainder)
print(min(4, 9, 2, 7))          # 2
print(max(4, 9, 2, 7))          # 9
print(sum([1, 2, 3, 4]))        # 10

divmod() returns the quotient and the remainder together as a tuple. This is useful for tasks like converting seconds to minutes and seconds.


5. The math Module: the Parts You Will Use Most

python
import math

print(math.pi)                  # 3.141592653589793
print(math.e)                   # 2.718281828459045

print(math.sqrt(2))             # 1.4142135623730951
print(math.floor(3.9))          # 3        (rounds down to a whole number)
print(math.ceil(3.1))           # 4        (rounds up to a whole number)
print(math.gcd(12, 18))         # 6        (the greatest common divisor)

# Logarithms
print(math.log(100, 10))        # 2.0      (log base 10)
print(math.log2(8))             # 3.0      (log base 2)

# Degrees / radians for trig (sin, cos, tan all expect radians)
print(math.radians(180))        # 3.14...  (180 degrees is π radians)
print(math.sin(math.pi / 2))    # 1.0

A quick "is this finite?" check (useful when dividing by user input):

python
import math

result = 1 / 0.0001
print(math.isfinite(result))    # True
print(math.isinf(float("inf"))) # True
print(math.isnan(float("nan"))) # True

6. Formatting Numbers for Display

You've seen f"{x:.2f}" in the Input/Output lesson. Here are the most common format specs:

python
n = 1234567.891

print(f"{n:.2f}")               # 1234567.89          (2 decimal places)
print(f"{n:,.2f}")              # 1,234,567.89        (with a thousands separator)
print(f"{n:.2e}")               # 1.23e+06            (scientific notation)
print(f"{n:>15.2f}")            # "     1234567.89"   (right-aligned in a width of 15)
print(f"{n:<15.2f}")            # "1234567.89     "   (left-aligned)

# Percent
print(f"{0.847:.1%}")           # 84.7%

# Integer with leading zeros
print(f"{7:04d}")               # 0007                (useful for IDs)

These format specs work the same way in format() calls outside f-strings.


7. Random Numbers

python
import random

# Integers
print(random.randint(1, 6))     # a six-sided die roll: 1-6 inclusive
print(random.randrange(0, 100)) # 0-99

# Floats
print(random.random())          # 0.0 ≤ x < 1.0
print(random.uniform(0, 10))    # 0.0 ≤ x < 10.0

# Pick from a collection
colors = ["red", "green", "blue", "yellow"]
print(random.choice(colors))    # one item
print(random.sample(colors, 2)) # two different items
random.shuffle(colors)          # shuffles the list in place
print(colors)

If you need the same "random" numbers on every run (for example, when testing), set a seed:

python
import random
random.seed(42)                 # the same seed gives the same numbers on every run
print(random.randint(1, 100))   # 82
print(random.randint(1, 100))   # 15

8. Mistakes You'll Hit

1. Comparing floats with ==

python
# 0.1 + 0.2 == 0.3 gives False (it looks like a bug, but it is not)
import math
math.isclose(0.1 + 0.2, 0.3)    # True  (this is the right way to compare floats)

2. Forgetting that round() uses banker's rounding

python
print(round(0.5))               # 0   (not 1!)
print(round(2.5))               # 2   (not 3!)

For traditional "0.5 rounds up", use math.floor(x + 0.5) or int(x + 0.5) for positive numbers.

3. Dividing integers and expecting an integer

python
print(10 / 3)                   # 3.3333... (always a float)
print(10 // 3)                  # 3         (floor division gives a whole number)


Mini-Program: Random Password Strength Meter

python
import math, random

# Make up a "password" length for this demo
password_length = 12
charset_size = 94               # printable ASCII characters, not counting space

# Entropy in bits: log2(charset_size^length)
entropy = password_length * math.log2(charset_size)
print(f"Estimated entropy: {entropy:.1f} bits")

# Time to crack at 1 billion guesses per second
seconds = (charset_size ** password_length) / 2 / 1_000_000_000
years = seconds / (60 * 60 * 24 * 365)
print(f"Average crack time: {years:.2e} years")

This program uses the math module and a very large integer to do a real calculation.


🎯 Your Turn: Split a Bill Without Losing a Paisa

Three friends split a ₹100 bill. 100 / 3 is 33.333..., and if you round each
share to 33.33 the total comes to 99.99. You have lost a paisa. Banks and
billing systems never lose that paisa.

Write split_bill(total_paise, people) that divides a whole number of paise
into people shares that add up to exactly the original amount. Give the
leftover paise to the earliest shares, one each.

python
split_bill(10000, 3)  →  [3334, 3333, 3333]     # sums to 10000
split_bill(10000, 4)  →  [2500, 2500, 2500, 2500]
split_bill(7, 2)      →  [4, 3]
split_bill(5, 5)      →  [1, 1, 1, 1, 1]

Skeleton:

python
def split_bill(total_paise, people):
    # TODO 1: the base share for everyone. Use // to stay in whole paise
    # TODO 2: how many paise are left over? use %
    # TODO 3: build the list, adding 1 paisa to the first `leftover` shares
    ...

shares = split_bill(10000, 3)
print(shares)        # [3334, 3333, 3333]
print(sum(shares))   # 10000  (must match the bill exactly)
Hint 1: Use only integers, from start to end Money in paise is a whole number, so never let a float in. // gives you the floor of the division and % gives you the remainder: 10000 // 3 is 3333 and 10000 % 3 is 1. That 1 is the paisa you must not lose.
Hint 2: The leftover is always smaller than people total % people can never be larger than people - 1, so you are always handing out at most one extra paisa per share. Build the list with a loop or a comprehension and ask, for each index, whether it is below the leftover count.
Show full solution
python
def split_bill(total_paise, people):
    base = total_paise // people
    leftover = total_paise % people
    return [base + (1 if i < leftover else 0) for i in range(people)]

# Sanity checks
print(split_bill(10000, 3))        # [3334, 3333, 3333]
print(sum(split_bill(10000, 3)))   # 10000
print(split_bill(7, 2))            # [4, 3]
print(split_bill(5, 5))            # [1, 1, 1, 1, 1]

The key is to never use floats. 100 / 3 in floating point is
33.333333333333336, which is already wrong in the last digit before you round it. By
working in paise with // and %, every value stays an exact integer and the
sum is guaranteed to match. This is why real payment systems store amounts as
integer minor units, not as float rupees.


What You Learned

  • Three numeric types: int (unlimited size), float (~15-17 digit precision), complex (rare).
  • Floats are inexact. Never compare with ==, use math.isclose().
  • abs, round, divmod, pow, min, max, sum are built in.
  • The math module has square roots, logarithms, trigonometry, constants.
  • Format numbers with f"{n:.2f}", f"{n:,}", f"{n:.1%}".
  • random gives you dice rolls, random choices, shuffles and samples. Set a seed when you need the same results in tests.

Next: Operators & Expressions.


Source: adapted from Python official documentation Section 3.1.1 (Numbers) and the math module reference. PSF License.

Practice this

on practicepython.in

Short exercises that run in your browser and tell you what your code did, as well as whether a test passed.

Found this useful? Share it with a friend who’s learning Python.