zlacker

[return to "My Favorite Programming Problem to Teach: Digit Length"]
1. jepler+N3[view] [source] 2019-11-10 22:44:41
>>jstrie+(OP)
But the math.log10 solution is unfortunately "wrong" too, at least in my python3 implementation.

    import math

    def digitLengthCorrect(n):
        return len(str(n))

    def digitLengthClever2(n):
        return 1 if n == 0 else (math.floor(math.log10(n)) + 1)

    testcases = (
        [10 ** i for i in range(300)] +
        [(10 ** i) - 1 for i in range(300)]
    )

    for t in testcases:
        a = digitLengthCorrect(t)
        b = digitLengthClever2(t)
        assert a == b, (t, a, b)
◧◩
2. jstrie+v5[view] [source] 2019-11-10 23:06:05
>>jepler+N3
Interesting point, could be worth adding a note to students about the finite precision of floating point numbers in Python. Thanks for pointing this out!
◧◩◪
3. jepler+K6[view] [source] 2019-11-10 23:19:51
>>jstrie+v5
Incidentally, while it focuses on values that fit in machine registers, you might check out Hacker's Delight's section on computing "ilog10". In the second edition, section 11-4 is on integer logarithms.

If your target language is Python3, you have the "bit_length()" method of integer objects. Multiply by c ~= 0.30103 and then correct for being too low; superficially, it seems like this algorithm should work for extremely large numbers, at least to millions of digits.

As far as test cases, n10, (n10)+1 and (n10)-1 seem like good choices and as long as the underlying mathematical function is monotonic should ferret out failures. I could easily run my implementation for numbers up to 30,000 digits, but going to 300,000 digits didn't finish while writing this message.

    log10_2 = math.log10(2)
    def digit_length_clever3(n):
        if not n: return 1
        est = math.floor(n.bit_length() * log10_2)
        low = n >= 10**est
        return est + low
[go to top]