Logarithms — The Simplest Idea with the Scariest Name

Logarithms sound intimidating, but you already understand them. Let's prove it.

Part 1: Counting Zeros

You already know what a logarithm is — you just don't know that you know it yet.

Exercise 1.1 — How Many Zeros?

Look at these numbers and count how many zeros each one has:

Your answer:

How many zeros does 10 have?

Your answer:

How many zeros does 100 have?

Your answer:

How many zeros does 1000 have?

Your answer:

Do you see the pattern?

Hint 1

10 has 1 zero, 100 has 2 zeros, 1000 has 3 zeros.

Solution

10 → 1 zero, 100 → 2 zeros, 1000 → 3 zeros, 10000 → 4 zeros.

Congratulations — you just computed log base 10! log(10) = 1, log(100) = 2, log(1000) = 3, log(10000) = 4. That's it. That's all a logarithm is.

Exercise 1.2 — Powers of 10 in Python

In Python, ** is the power operator. 10**2 means "10 to the power of 2" which is 100.

Try these in Python and observe the pattern:

What do you notice? Each power adds one more zero!

Hint 1

10**1 is 10, 10**2 is 100, 10**3 is 1000. The power tells you how many zeros.

Solution
print(10**1)   # 10
print(10**2)   # 100
print(10**3)   # 1000
print(10**4)   # 10000

Exercise 1.3 — Predict the Log

Now go the other way. If someone asks "what is log(100000)?", you are really asking: "how many zeros does 100000 have?" or equivalently "10 to the power of WHAT gives 100000?"

Predict the answer, then verify by computing 10**your_answer in Python.

Provided — verify
# Replace the ? with your answer
my_answer = ?
print(f"10**{my_answer} = {10**my_answer}")
# It should print 100000
Hint 1

Count the zeros in 100000. There are 5 of them.

Solution
my_answer = 5
print(f"10**{my_answer} = {10**my_answer}")
# 10**5 = 100000

What you just did is called "taking the logarithm base 10." When you said log(1000) = 3, you were saying: "the power of 10 that gives 1000 is 3." Simple, right?

Part 2: The Power Question

Let's reframe logarithms as a question: "10 to the power of WHAT gives this number?"

Exercise 2.1 — Flip It Around

Answer each question without Python — just think:

Your answer:

10 to the power of what gives 100?

Your answer:

10 to the power of what gives 1000?

Your answer:

10 to the power of what gives 10?

Your answer:

10 to the power of what gives 1? (Tricky! What happens with power 0?)

Hint 1

Remember: any number to the power of 0 is 1. So 10**0 = 1, which means log(1) = 0.

Solution
  • 10^2 = 100, so the answer is 2.
  • 10^3 = 1000, so the answer is 3.
  • 10^1 = 10, so the answer is 1.
  • 10^0 = 1, so the answer is 0.

Exercise 2.2 — What About 500?

Here's where it gets interesting. What is log(500)?

You know log(100) = 2 and log(1000) = 3. So log(500) must be somewhere between 2 and 3. But where exactly?

Use Python's ** operator to do hit-and-trial. Try different values of x and see if 10**x gets close to 500.

Provided — try it
# Try different values of x until 10**x is close to 500
x = 2.5
print(f"10**{x} = {10**x}")

# Keep adjusting x...
# Is 10**2.5 too big or too small? Try another value.
Hint 1

10**2.5 is about 316 — too small. Try something bigger.

Hint 2

10**2.7 is about 501 — very close! The answer is around 2.7.

Solution
print(f"10**2.5 = {10**2.5}")   # ~316 — too small
print(f"10**2.6 = {10**2.6}")   # ~398 — still small
print(f"10**2.7 = {10**2.7}")   # ~501 — very close!
print(f"10**2.69 = {10**2.69}") # ~490 — a bit under
print(f"10**2.699 = {10**2.699}") # ~500.3 — almost!

Exercise 2.3 — Check with Python's Built-in

Python has a built-in function math.log10() that computes the exact log base 10. Let's check how close your hit-and-trial answer was.

Provided — check
import math
print(f"log10(500) = {math.log10(500)}")
# Compare with your answer from the previous exercise!
Hint 1

math.log10(500) gives approximately 2.699. If your trial got close to 2.7, you did great!

Solution
import math
print(f"log10(500) = {math.log10(500)}")
# 2.6989700043360187

Exercise 2.4 — More Hit-and-Trial

Now try the same hit-and-trial approach for these:

  1. What is log10(50)? (It's between 1 and 2. Try values of x where 10**x gets close to 50.)
  2. What is log10(5)? (It's between 0 and 1.)

After your guesses, verify both with math.log10().

Provided — verify
import math
# After your trials, check:
print(f"log10(50) = {math.log10(50)}")
print(f"log10(5)  = {math.log10(5)}")
Hint 1

For 50: try 10**1.5 (about 31.6), 10**1.7 (about 50.1) — so log10(50) is about 1.7.

Hint 2

For 5: try 10**0.5 (about 3.16), 10**0.7 (about 5.01) — so log10(5) is about 0.7.

Hint 3

Notice something? log10(5) ≈ 0.699 and log10(50) ≈ 1.699. The difference is exactly 1! That's because 50 = 5 × 10, and multiplying by 10 adds 1 to the log.

Solution
import math
# Hit and trial for 50:
print(f"10**1.5 = {10**1.5}")   # ~31.6
print(f"10**1.7 = {10**1.7}")   # ~50.1 — close!
print(f"Exact: log10(50) = {math.log10(50)}")  # 1.699

# Hit and trial for 5:
print(f"10**0.5 = {10**0.5}")   # ~3.16
print(f"10**0.7 = {10**0.7}")   # ~5.01 — close!
print(f"Exact: log10(5) = {math.log10(5)}")    # 0.699

Key insight: log10(5) ≈ 0.699, log10(50) ≈ 1.699, log10(500) ≈ 2.699. Each 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!

Part 3: Beyond Base 10 — Any Base Works

So far we've only used 10 as the base. But there's nothing special about 10 — it's just what humans picked because we have 10 fingers. Logarithms work with ANY base.

Exercise 3.1 — Log Base 2

Instead of asking "10 to the power of what?", let's ask "2 to the power of what?"

This is called log base 2, written as log2.

Your answer:

log2(4) = ? In other words: 2 to the power of what gives 4?

Your answer:

log2(8) = ? i.e. 2**x = 8, what is x?

Your answer:

log2(16) = ?

Your answer:

log2(2) = ?

Hint 1

2**2 = 4, so log2(4) = 2.

Hint 2

2**3 = 8, so log2(8) = 3.

Solution
  • log2(4) = 2, because 2^2 = 4.
  • log2(8) = 3, because 2^3 = 8.
  • log2(16) = 4, because 2^4 = 16.
  • log2(2) = 1, because 2^1 = 2.

Exercise 3.2 — Verify with Python

Use Python's ** operator to verify your answers. Compute 2**2, 2**3, 2**4, and 2**1.

Then try: what is log2(32)? log2(64)? log2(1024)?

Hint 1

2**5 = 32, so log2(32) = 5.

Hint 2

2**10 = 1024, so log2(1024) = 10. Computers love this one — 1 KB is 1024 bytes!

Solution
print(f"2**1 = {2**1}")    # 2
print(f"2**2 = {2**2}")    # 4
print(f"2**3 = {2**3}")    # 8
print(f"2**4 = {2**4}")    # 16
print(f"2**5 = {2**5}")    # 32
print(f"2**6 = {2**6}")    # 64
print(f"2**10 = {2**10}")  # 1024

Exercise 3.3 — The Power of Zero

What is log2(1)?

Hint: what is ANY number raised to the power of 0? Try 2**0, 10**0, 5**0 in Python.

Hint 1

Any number raised to the power 0 is 1. So log of 1 is always 0, no matter what the base is.

Solution
print(f"2**0 = {2**0}")    # 1
print(f"10**0 = {10**0}")  # 1
print(f"5**0 = {5**0}")    # 1
# So log2(1) = 0, log10(1) = 0, log5(1) = 0
# log of 1 is ALWAYS 0, regardless of base!

Exercise 3.4 — Other Bases

Let's try other bases:

  1. What is log3(27)? i.e. 3 to the power of what gives 27?
  2. What is log5(125)? i.e. 5 to the power of what gives 125?
  3. What is log4(64)?

Compute using ** first, then verify with Python's math.log(value, base).

Provided — verify
import math
# math.log(value, base) computes log of value with given base
print(f"log3(27)  = {math.log(27, 3)}")
print(f"log5(125) = {math.log(125, 5)}")
print(f"log4(64)  = {math.log(64, 4)}")
Hint 1

3**3 = 27, so log3(27) = 3.

Hint 2

5**3 = 125, so log5(125) = 3.

Hint 3

4**3 = 64, so log4(64) = 3. All three are 3!

Solution
# Verify by computing powers:
print(f"3**3 = {3**3}")    # 27
print(f"5**3 = {5**3}")    # 125
print(f"4**3 = {4**3}")    # 64

import math
print(f"log3(27)  = {math.log(27, 3)}")   # 3.0
print(f"log5(125) = {math.log(125, 5)}")   # 3.0
print(f"log4(64)  = {math.log(64, 4)}")    # 3.0

The pattern: log_base(value) always asks the same question — "base to the power of WHAT gives value?" It doesn't matter if the base is 2, 3, 5, 10, or anything else. The idea is always the same.

Part 4: Fractional Logs — Between the Integers

So far all our answers have been nice whole numbers. But what happens when the answer isn't a whole number?

Exercise 4.1 — Narrowing It Down

What is log2(6)?

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

But where between 2 and 3?

Your answer:

Is log2(6) closer to 2 or to 3? (Is 6 closer to 4 or to 8?)

Your answer:

Try 2**2.5 in your head or on paper. Is it bigger or smaller than 6?

Hint 1

6 is closer to 8 than to 4, so log2(6) is closer to 3 than to 2. It's probably around 2.5 to 2.6.

Hint 2

2**2.5 = 2^2 × 2^0.5 = 4 × 1.414... ≈ 5.66 — a bit less than 6. So the answer is a bit above 2.5.

Solution

log2(6) is between 2 and 3. Since 2**2.5 ≈ 5.66 (less than 6), the answer is a bit above 2.5.

Exercise 4.2 — Hit-and-Trial for log2(6)

Use 2**x with different values of x to get as close to 6 as you can. Then check your answer with math.log(6, 2).

Provided — try it
import math

# Try different values of x
x = 2.5
print(f"2**{x} = {2**x}")

# Keep adjusting...

# When you're done, check the exact answer:
print(f"\nExact: log2(6) = {math.log(6, 2)}")
Hint 1

2**2.5 ≈ 5.66 — too small. 2**2.6 ≈ 6.06 — a tiny bit too big. So it's between 2.5 and 2.6.

Hint 2

Try 2**2.58 ≈ 5.98. The exact answer is about 2.585.

Solution
import math
print(f"2**2.5  = {2**2.5}")    # ~5.66
print(f"2**2.6  = {2**2.6}")    # ~6.06
print(f"2**2.58 = {2**2.58}")   # ~5.98
print(f"2**2.585 = {2**2.585}") # ~5.999 — very close!

print(f"\nExact: log2(6) = {math.log(6, 2)}")  # 2.5849...

Exercise 4.3 — Automate the Trial — Write my_log()

You've been doing hit-and-trial by hand. Let's make Python do it for you!

Write a function my_log(value, base) that finds the log by brute force: try x from 0 to 20 in small steps (say 0.001) and return the x where base**x is closest to value.

Provided — test your function
print(f"my_log(1000, 10) = {my_log(1000, 10)}")  # should be ~3.0
print(f"my_log(8, 2)     = {my_log(8, 2)}")      # should be ~3.0
print(f"my_log(6, 2)     = {my_log(6, 2)}")      # should be ~2.585
print(f"my_log(500, 10)  = {my_log(500, 10)}")    # should be ~2.699
Hint 1

Start with best_x = 0 and best_diff = abs(value - 1) (since base**0 = 1). Loop x from 0 to 20 in steps of 0.001. If abs(base**x - value) is smaller than best_diff, update best_x and best_diff.

Hint 2

The structure looks like:

def my_log(value, base):
    best_x = 0
    best_diff = abs(value - 1)
    x = 0
    while x <= 20:
        diff = abs(base**x - value)
        if diff < best_diff:
            ...
        x += 0.001
    return best_x
Solution
def my_log(value, base):
    best_x = 0
    best_diff = abs(value - 1)
    x = 0
    while x <= 20:
        diff = abs(base**x - value)
        if diff < best_diff:
            best_diff = diff
            best_x = x
        x += 0.001
    return round(best_x, 3)

What you just built is a brute-force logarithm calculator! It's slow (checking 20,000 values), but it works. In the Binary Search chapter, you'll learn how to find this answer in just 40 steps instead of 20,000 — that's the power of binary search.

Summary

Here's everything you discovered in this chapter.

Logarithm = the reverse of exponentiation.

When someone says log_b(x) = y, they're saying: b to the power of y equals x.

That's it. That's the whole idea. Now when you see log2(1024) = 10, you know it just means 2^10 = 1024.

Logs You Computed

Question Meaning Answer
log10(10) 10 to the power of ? = 10 1
log10(100) 10 to the power of ? = 100 2
log10(1000) 10 to the power of ? = 1000 3
log10(500) 10 to the power of ? = 500 ~2.699
log2(4) 2 to the power of ? = 4 2
log2(8) 2 to the power of ? = 8 3
log2(1024) 2 to the power of ? = 1024 10
log3(27) 3 to the power of ? = 27 3
log of 1 (any base) any number to the power of ? = 1 0