Estimating Logarithms
While reading through the fantastic book The Lost Art of Logarithms by Charles Petzold I was nerd-sniped by a simple method of estimating the logarithm of any number base 10. According to the book, it was developed by John Napier (the father of the logarithm) about 1615.
In french the natural logarithm is also called “le logarithm népérien” in reference to the mathematician.
The Method
We note that due to the nature of the logarithm (always referring to base 10 from here one out), the logarithm of any number
This approximation by itself might seem useless at first: knowing that the logarithm of 5 is between 0 and 1 is pointless. But in combination with the following property of logarithms:
We can calculate the logarithm of any number with arbitrary precision using the following this algorithm. We note
Increase the exponent from 100 to 1000 and you’ve added another digit of precision.
Henry Briggs used this method to compute the logarithms of 2 and 7 to the 14th digit. Calculating
Simply exchanging the complexity of calculating the logarithm with an extremely tedious exponentiation isn’t very useful, we are lacking one more insight…
One more Trick
Fortunately, between the 17th century and today, mathematicians came up with something that makes this task a lot easier: scientific notation.
Knowing that
By repeating these steps and keeping previous results, we only ever have to multiply the mantissa (always smaller than 10) with itself and the exponent by 2. After only 10 repetitions of exponentiating by 2 (a total of 20 rather trivial multiplications) we’ll have 4 digits of precision.
In Code
This is still a lot of work to do by hand and thus I went ahead and wrote a little python script that starts from
import math
import decimal
decimal.getcontext().prec = 100
def get_scientific(num):
num = decimal.Decimal(num)
# Calculate length of the number in a 'naive' way, without using the log
# Using log10 here would kind of defeat the point
# Because the length never exceeds 10, counting them manually is always possible
length = len(str(math.floor(num))) - 1
mantissa = num / (10 ** length)
return mantissa, length
def count_digits(num, precision: int):
assert(precision > 0)
mantissa, exponent = get_scientific(num ** 10)
# Apply the trick here by doing the calculation iteratively
for _ in range(precision - 1):
# Use the properties of exponents
# (m x 10^exp)^10 = m^10 x 10^(10 * exp)
mantissa = mantissa ** 10
exponent *= 10
mantissa, new_exponent = get_scientific(mantissa)
# Calculate the contribution of the new mantissa to the exponent and add it
exponent += new_exponent
return exponent
def logarithm(num, precision):
# Count the number of digits
n_digits = count_digits(num, precision)
# Divide by the exponent
result = n_digits / decimal.Decimal(10 ** precision)
return f"{result:.{precision}f}"
For each additional digit of precision, it calculates Rinse keep the previous results and repeat.
I’m not super happy with the implementation because of the decimal
import which could probably be made unnecessary by doing some string manipulations.
I wrote this little post in order to remember this neat little trick better and to maybe expose some people who wouldn’t have read chapter 4 in the book to it. I can only recommend checking out the content straight from the source, online and for free!
What's Your Reaction?






