Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix my numberOfDigits function

Came across some code where the number of digits was being determined by casting the number to a string then using a len().

Function numOfDigits_len(n As Long) As Long
    numOfDigits_len = Len(Str(n)) - 1
End Function

Now although this works I knew it would be slow compared to any method that didn't use strings, so I wrote one that uses log().

Function numOfDigits_log(n As Long) As Long
    numOfDigits_log = Int(Log(n) / Log(10)) + 1
End Function

Cut run time by 1/2 which was great but there was something weird happening in a specific case.

  n     numOfDigits_log(n)
=====  ====================
 999            3
1000            3
1001            4

It would not handle 1000 properly. I figured it is because of floating point and rounding issues.

Function numOfDigits_loop(ByVal n As Long) As Long
    Do Until n = 0
        n = n \ 10
        numOfDigits_loop = numOfDigits_loop + 1
    Loop
End Function

Wrote this which turned out to be ~10% slower as numbers got larger than 10^6 and seems to become slowly larger as n gets bigger. Which is fine if I was being pragmatic but I would like to find something more ideal.

Now my question is, is there a way to use the log() method accurately. I could do something like

Function numOfDigits_log(n As Long) As Long
    numOfDigits_log = Int(Log(n) / Log(10) + 0.000000001) + 1
End Function

But it seems very "hacky". Is there a nicer way that's faster or as fast as the log() method? Note: I realize this kind of optimization is pointless in a lot of cases but now that I've come across this I would like to "fix" it

like image 913
ashareef Avatar asked Jun 19 '13 20:06

ashareef


2 Answers

I've answered this before, but I couldn't find it, so here's the basics:

int i = ... some number >= 0 ...
int n = 1;
if (i >= 100000000){i /= 100000000; n += 8;}
if (i >= 10000){i /= 10000; n += 4;}
if (i >= 100){i /= 100; n += 2;}
if (i >= 10){i /= 10; n += 1;}

That's in C, but you get the idea.

like image 172
Mike Dunlavey Avatar answered Oct 20 '22 19:10

Mike Dunlavey


A while loop guarantees correctness, i.e. it doesn't use any floating point calculations

int numDigits = 0;
while(num != 0) {
    num /= 10;
    numDigits++;
}

You can also speed this up by using a larger divisor

int numDigits = 0;
if(num >= 100000 || num <= -100000) {
    int prevNum;
    while(num != 0) {
        prevNum = num;
        num /= 100000;
        numDigits += 5;
    }
    num = prevNum;
    numDigits -= 5;
}
while(num != 0) {
    num /= 10;
    numDigits++;
}
like image 1
Zim-Zam O'Pootertoot Avatar answered Oct 20 '22 19:10

Zim-Zam O'Pootertoot