I am somewhat perplexed by this (presumably very simple) issue.

Simply, I am trying to find the leading digit(s) in any number (related question). Here is some nice python code (from this question):

import math

def first_n_digits(num, n):
    # where n is the number of leading digits of interest
    return num // 10 ** (int(math.log(num, 10)) - n + 1)

This works great for most numbers, for example:

>>> first_n_digits(123456, 1)
>>> first_n_digits(123456, 2)
>>> first_n_digits(123456, 3)
>>> first_n_digits(123456, 4)
>>> first_n_digits(123456, 5)
>>> first_n_digits(123456, 6)

The problem is this does not work for num=1000 and n=1.

first_n_digits(1000, 1)

This issue does not appear when num = 10,100,10000,100000. Just num = 1000.

How can one deal with this edge case? I have not found any others so far. I expect there to be some very simple solution which I have overlooked.

  • $\begingroup$ Could you take log10 of $n+1/2$ to avoid the tiny errors $\endgroup$ – Empy2 Oct 12 '18 at 10:17

The problem is simply rounding errors. The operation int when applied to a (positive?) float, rounds the number down to the nearest integer. This works fine if we pretend that the output of math.log is exact, but it is not:

>>> math.log(1000, 10)
>>> int(math.log(1000, 10))

There are ways to fix this, e.g. by testing what happens if you compute 10 to the power of the output of this computation, but the more general difficulty is that you can never guarantee that floating point operations will be exact, simply because it's impossible to represent all possible real numbers on a computer.

In fact, the Pythonic way to solve your problem is the entirely robust function (at least for positive integers, and assuming n takes on a reasonable value)

def first_n_digits(num, n):
    return int(str(num)[:n])
  • $\begingroup$ Thanks for that explanation, very helpful. I did know about that solution but was trying to avoid it because it takes a lot more operations. But fair enough, it is very robust, this is true. $\endgroup$ – Astrid Oct 12 '18 at 9:35

Python documentation for math.log says

With two arguments, return the logarithm of x to the given base, calculated as log(x)/log(base).

So it is possible that $\text{math.log}(1000,10)$ is returning a value very slightly less than 3 due to rounding errors, and when you apply int to this you get $2$. Try evaluating $\text{int}(\text{math.log}(1000,10))$ to see whether this evaluates to $2$ or $3$. (I see Mees de Vries has already done this test).

There is an alternative function in the math module called math.log10:

Return the base-10 logarithm of x. This is usually more accurate than log(x, 10).

  • $\begingroup$ Note that this can still fail, although it works much longer: in Python 3.6.5, int(math.log10(10**512)) == 511. $\endgroup$ – Mees de Vries Oct 12 '18 at 14:51

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.