Logarithms

Logarithms sound intimidating, but you already understand them. You’ll prove it to yourself in the next few minutes — starting with nothing more than counting zeros.

Part 1: Counting Zeros~5 min

Look at these numbers:

NumberHow many zeros?
10?1
100?2
1000?3
10000?4
100000?5

How many zeros does 10 have?

That’s right! 10 has 1 zero.

How many zeros does 100 have?

That’s right! 100 has 2 zeros.

How many zeros does 1000 have?

That’s right! 1000 has 3 zeros.

How many zeros does 10000 have?

That’s right! 10000 has 4 zeros. Do you see the pattern? Each time we add a zero, the count goes up by 1.

How many zeros does 100000 have?

That’s right! 100000 has 5 zeros.

This “number of zeros” has a fancy name: logarithm.

In Python, ** is the power operator. 10**2 means “10 to the power of 2”.

What does each expression give?

ExpressionValueLog
10**1?101
10**2?1002
10**3?10003
10**4?100004

The power tells you how many zeros. The number of zeros tells you the power.

Going from power → number is exponentiation.
Going from number → power is logarithm.

So when someone asks “what is log(1000)?” they are really asking:

10 to the power of what gives 1000?

What is the answer?

That’s right! The answer is 3, because 10 × 10 × 10 = 103 = 1000.

That’s it. That’s all a logarithm is. Let’s fill out a few more:

QuestionIn other wordsAnswer
log(10)10? = 10?1
log(100)10? = 100?2
log(1000)10? = 1000?3
log(10000)10? = 10000?4

Part 2: The Power Question~5 min

Quick fire: answer without Python.

10 to the power of what gives 100?

Think: how many times do you multiply 10 by itself to get 100?

That’s right! 2, because 10 × 10 = 102 = 100.

10 to the power of what gives 10?

That’s right! 1, because 101 is just 10 itself.

10 to the power of what gives 1?

(Tricky! What happens when the power is 0?)

That’s right! 0, because any number to the power of 0 is 1. Try 10**0 in Python — you get 1.

So what is log(1)?

That’s right! log(1) = 0.

Key fact: log of 1 is always 0, no matter what the base is. Because anything0 = 1.

What is log(500)?

In other words: 10 to the power of what gives 500?

It’s not a whole number. But you already know something about it.

102 = 100→ too small
103 = 1000→ too big

So log(500) is between 2 and 3.

Just take a guess!

Part 3: The Guessing Game~10 min

You know log(500) is between 2 and 3. Let’s find it by guessing, checking, and narrowing down.

Check your guess: compute 10**x in Python.

guess = 2.5
print(10**guess)

316.23

316 is less than 500. Your guess was too low!

So the answer is between …2.5 and 3. Take a better guess!

guess = 2.75
print(10**guess)

562.34

Too high! Answer is between 2.5 and 2.75.

guess = 2.625
print(10**guess)

421.70

Too low! Answer is between 2.625 and 2.75.

Your next guess will be between which two numbers?

Call them lo and hi, then calculate the midpoint:

lo = 2
hi = 3

guess = (lo + hi) / 2
print(f"Guess: {guess}")
print(f"10**guess: {10**guess}")

Guess: 2.5
10**guess: 316.23

Too low! So the answer is between guess and hi.

Update lo = guess and compute a new midpoint.

Can you automatically change lo or hi depending on whether 10**guess was higher or lower than 500?

This is Python’s if-else:

lo = 2
hi = 3
value = 500

guess = (lo + hi) / 2

if 10**guess > value:
    # guess was too high — which bound should change?
else:
    # guess was too low — which bound should change?

print(f"New range: [{lo}, {hi}]")
lo = 2
hi = 3
value = 500

guess = (lo + hi) / 2

if 10**guess > value:
    hi = guess       # too high — bring ceiling down
else:
    lo = guess       # too low — raise the floor

print(f"New range: [{lo}, {hi}]")

New range: [2.5, 3]

Run the logic again and again. Do you see your guess getting closer?

Roundlohiguess10guess 
1232.5316.23↑ too low
22.532.75562.34↓ too high
32.52.752.625421.70↑ too low
42.6252.752.6875486.97↑ too low
52.68752.752.7188523.30↓ too high

Each guess cuts the range in half. After 20 rounds, the range is smaller than 0.000001.

But copying this block 20 times is tedious…

Part 4: Let the Loop Do the Work~10 min

Copying and pasting the same block over and over is tedious. Python’s for loop can repeat it for you — as many times as you want.

Run your logic 20 times using a for loop:

lo = 2
hi = 3
value = 500

for i in range(20):
    guess = (lo + hi) / 2
    # Use your if-else logic here to update lo or hi

print(f"log({value}) = {guess}")
print(f"10**guess = {10**guess}")
lo = 2
hi = 3
value = 500

for i in range(20):
    guess = (lo + hi) / 2
    if 10**guess > value:
        hi = guess
    else:
        lo = guess

print(f"log({value}) = {guess}")
print(f"10**guess = {10**guess}")

log(500) = 2.6989700043360187
10**guess = 500.0000000000002

Did 10**guess get close to 500? Let’s compare with Python’s built-in:

import math
print(f"Your answer:     {guess}")
print(f"math.log10(500): {math.log10(500)}")

Your answer:     2.6989700043360187
math.log10(500): 2.6989700043360187

They match! Your simple guessing loop matches the built-in function.

Now repeat the process for log(50).

What should lo and hi start at?

101 = 10 (too small), 102 = 100 (too big) → lo = 1, hi = 2

Run the same loop — it works!

Every guess eliminates half of the remaining range.

After 1 guess:half the range left
After 5 guesses:1/32 of the range
After 10 guesses:1/1024 of the range
After 20 guesses:1/1,048,576 of the range

This “halving” strategy is called binary search — “binary” because you split the range in two each time.

It is one of the most important algorithms in computer science — and you just invented it.

Part 5: Build Your Own Log Function~5 min

You’ve found log(500) and log(50) by repeating the same code with different numbers. Copying code is messy — a function packages it so you can call it with any input.

Consolidate your for-loop and if-else into one function. Fill in the two missing lines:

def my_log(value, base, lo, hi, iterations):
    for i in range(iterations):
        guess = (lo + hi) / 2
        if base**guess > value:
            pass   # Your code goes here
        else:
            pass   # Your code goes here
    return guess
def my_log(value, base, lo, hi, iterations):
    for i in range(iterations):
        guess = (lo + hi) / 2
        if base**guess > value:
            hi = guess
        else:
            lo = guess
    return guess

When base**guess > value (too high), bring the ceiling down: hi = guess.

When too low, raise the floor: lo = guess.

Same logic you’ve been doing all along — now packaged as a reusable function.

Test it:

import math

result = my_log(500, 10, 2, 3, 20)
print(f"my_log:      {result}")
print(f"math.log10:  {math.log10(500)}")

my_log:      2.6989700043360187
math.log10:  2.6989700043360187

They match!

Try more:

print(my_log(1000, 10, 2, 4, 20))  # 3.0
print(my_log(8, 2, 2, 4, 20))      # 3.0
print(my_log(6, 2, 2, 3, 20))      # 2.5849625...
print(my_log(27, 3, 2, 4, 20))     # 3.0

Your function works for any base — because the condition base**guess > value works no matter what the base is!

Use your function to explore a pattern:

Numberlog10
50.699
501.699
5002.699
5000?3.699

Every time you multiply the number by 10, the log goes up by exactly 1.

That makes sense — you’re adding one more power of 10!

Congratulations! In one sitting you learned:

What you didPython concept
Counted zeros (10, 100, 1000)Expressions
Guessed log(500), checked with 10**xVariables
Tracked the range with lo and hiVariables (lo, hi)
Automated the high/low decisionif / else
Repeated the guessing 20 timesfor loop
Packaged it as my_logFunctions (def)

The algorithm you built — binary search — doesn’t just work for logarithms. It’s one of the most important algorithms in computer science.

And you just invented it.

Part 6: Beyond Base 10~10 min

Up to now, every “log” you computed was secretly log base 10 — counting zeros only works because our number system is built on powers of 10. From here on we’ll write the base explicitly, like log2 or log3, because the base is about to change.

There’s nothing special about 10. Let’s try base 2.

log2(x) asks: 2 to the power of what gives x?

Questionlog2
2? = 4?2
2? = 8?3
2? = 16?4
2? = 32?5
2? = 1024?10

log2(4) = ? How many times do you multiply 2 by itself to get 4?

That’s right! 2, because 2 × 2 = 4.

log2(8) = ? One more factor of 2…

That’s right! 3, because 2 × 2 × 2 = 8.

log2(16) = ?

That’s right! 4, because 24 = 16.

log2(32) = ? Keep doubling: 16 × 2 = 32.

That’s right! 5, because 25 = 32.

log2(1024) = ? Computers love this one — 1 KB is 1024 bytes!

That’s right! 10, because 210 = 1024. You need to multiply 2 by itself 10 times to reach 1024.

It works with any base. The question is always:

base? = value

QuestionAnswer
3? = 27?3
5? = 125?3
4? = 64?3
7? = 1?0

log3(27) = ? Think: 3 × 3 × 3 = ?

That’s right! 3, because 3 × 3 × 3 = 27.

log5(125) = ? Think: 5 × 5 × 5 = ?

That’s right! 3, because 5 × 5 × 5 = 125.

log4(64) = ? Think: 4 × 4 × 4 = ?

That’s right! 3 again, because 43 = 64. Notice all three were 3 — but the bases and values were different!

log7(1) = ? Remember: any number to the power of 0 is…

That’s right! 0, because 70 = 1. This works for every base: anything0 = 1.

Verify in Python: math.log(27, 3) → 3.0,  math.log(125, 5) → 3.0,  math.log(64, 4) → 3.0

What is log2(6)?

In other words: 2 to the power of what gives 6?

You know log2(4) = 2 and log2(8) = 3. Since 6 is between 4 and 8, the answer must be between 2 and 3.

You already have a function for this!

print(my_log(6, 2, 2, 3, 20))

2.584962500721156

log2(6) ≈ 2.585.

Verify: math.log(6, 2) → 2.585. Your binary search nails it!

Part 7: The Change of Base Formula~5 min

Python gives you math.log10() and math.log2() built in. But what if you need log base 3? Or base 5?

Try this experiment. For each row, divide log10(value) by log10(base):

Wantlog10(value)log10(base)ratioexpected
log3(27)1.4310.477?3.03
log2(8)0.9030.301?3.03
log5(125)2.0970.699?3.03

That’s right! 1.431 ÷ 0.477 = 3.0 — exactly what we expected!

Try the next row: 0.903 ÷ 0.301 = ?

That’s right! 3.0 again. The pattern holds.

One more: 2.097 ÷ 0.699 = ?

That’s right! 3.0 every time. Dividing log10(value) by log10(base) always gives the correct log!

You just discovered the change of base formula:

logb(x) = log10(x) ÷ log10(b)

This means you can compute a log in any base using only log10:

import math
def log_base(value, base):
    return math.log10(value) / math.log10(base)

Test it:

log_base(27, 3) → 3.0  ✓   log_base(6, 2) → 2.585  ✓   log_base(1024, 2) → 10.0  ✓

The base in the denominator can be anything — log2, log10, ln — the ratio is always the same.

(Python’s math.log(value, base) does exactly this internally.)

What you just invented:

You didReal name
Counted zeros in 10, 100, 1000…log10
“base to the power of what?”Logarithm definition
Guessing and narrowing downBinary search
my_log(value, base, lo, hi, iters)Binary-search log calculator
Dividing two log10 valuesChange of base formula

You didn’t learn logarithms. You invented them.

Glossary

Logarithm
The reverse of exponentiation: log_b(x) = y means b^y = x.
Base
The number being raised to a power. In log₁₀, the base is 10; in log₂, it's 2.
Exponent / Power
How many times the base is multiplied by itself: 10³ means 10 × 10 × 10.
Binary Search
Finding an answer by repeatedly halving the search range — guess the middle, eliminate the wrong half, repeat.
Change of Base
log_b(x) = log_a(x) / log_a(b) — convert any log to a different base.