tags:

views:

1670

answers:

10

I fear there's a simple and obvious answer to this question. I need to determine how many digits wide a count of items is, so that I can pad each item number with the minimum number of leading zeros required to maintain alignment. For example, I want no leading zeros if the total is < 10, 1 if it's between 10 and 99, etc.

One solution would be to cast the item count to a string and then count characters. Yuck! Is there a better way?

Edit: I would not have thought to use the common logarithm (I didn't know such a thing existed). So, not obvious - to me - but definitely simple. This question has generated way more activity than I expected. I'll let the dust settle before I choose a best answer.

+22  A: 
 int length = (number ==0) ? 1 : (int)Math.log10(number) + 1;

should do it.

Nicholas Mancuso
he said it was for a count. which implies the domain is over the natural numbers, no? so no need for negative check.
Nicholas Mancuso
Last time I checked, the number zero has one digit, not zero. So shouldn't number==0 imply length = 1?
Rob Kennedy
lol. duh. fixed it.
Nicholas Mancuso
So maybe simply calling n.toString().length() or n.abs().toString().length() isn't such a bad idea after all. Certainly it would be easier to understand for someone without a math background.
Metaphile
Wow.. it is quite intuitive.. Never thought about using Log10...
Sung Meister
A: 

One solution is provided by base 10 logarithm, a bit overkill.

mouviciel
+1  A: 

You can use a while loop, which will likely be faster than a logarithm because this uses integer arithmetic only:

int len = 0;
while (n > 0) {
    len++;
    n /= 10;
}

I leave it as an exercise for the reader to adjust this algorithm to handle zero and negative numbers.

Greg Hewgill
This is a good compromise. A "half-hearted log(n)". It's going to be more efficient than converting to string, since the conversion uses this as part of its algorithm.
gbarry
fyl2x is only something like 20-100 cycles. div takes about 40, so it will only take cases of 3-4 digits before this algorithm is at risk of being slower than a log. In the best cases log is faster than one step through this loop...
jheriko
@jheriko: Good point. For raw performance I suspect that your static array solution, or even a sequence of inline compares with immediate values, might prove to be the fastest. But really, it's splitting hairs at this point!
Greg Hewgill
indeed, in practice log might even be faster than what i suggested :)
jheriko
A: 

You can loop through and delete by 10, count the number of times you loop;

int num = 423;
int minimum = 1;
while (num > 10) {
    num = num/10;
    minimum++;
}
achinda99
+9  A: 
int length = (int)Math.Log10(Math.Abs(number)) + 1;

You may need to account for the negative sign..

Ryan Emerle
+1, HOWEVER This fails when number==0. You need to special case this.
Jason Cohen
+3  A: 

If you are going to pad the number in .Net, then

num.ToString().PadLeft(10, '0')

might do what you want.

ck
My interpretation is that he has a *list* of numbers, and he wants to pad the smaller ones to match the width of the largest one. So he still needs to figure out what to pass for the first parameter to PadLeft.
Rob Kennedy
+1  A: 

Since a number doesn't have leading zeroes, you're converting anyway to add them. I'm not sure why you're trying so hard to avoid it to find the length when the end result will have to be a string anyway.

Ken White
I like to explore alternative solutions. Sometimes I learn things - like log10.
Metaphile
I agree it's good to learn other ways. I just couldn't see why you would do so in such an obvious case when it wasn't needed. :-) As I said, if you know the end result will be a string, why waste time working out a way to not go ahead and do the conversion and get it done?
Ken White
A: 

Okay, I can't resist: use /=:

#include <stdio.h>

int
main(){
        int num = 423;
        int count = 1;
        while( num /= 10)
                count ++;
        printf("Count: %d\n", count);
        return 0;
}
534 $ gcc count.c && ./a.out
Count: 3
535 $
Charlie Martin
+3  A: 

A more efficient solution than repeated division would be repeated if statements with multiplies... e.g. (where n is the number whose number of digits is required)

unsigned int test = 1;
unsigned int digits = 0;
while (n >= test)
{
  ++digits;
  test *= 10;
}

If there is some reasonable upper bound on the item count (e.g. the 32-bit range of an unsigned int) then an even better way is to compare with members of some static array, e.g.

// this covers the whole range of 32-bit unsigned values
const unsigned int test[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };

unsigned int digits = 0;
while(n >= test[digits]) ++digits;
jheriko
I think this is the clearest, most efficient solution. However your array should start with 0, as 0 has 1 digit.
Rob Elliott
+2  A: 

I would have posted a comment but my rep score won't grant me that distinction.

All I wanted to point out was that even though the Log(10) is a very elegant (read: very few lines of code) solution, it is probably the one most taxing on the processor.

I think jherico's answer is probably the most efficient solution and therefore should be rewarded as such.

Especially if you are going to be doing this for a lot of numbers..

thanks for the backup, but if the majority prefer the logarithm solution it is their choice. it may be slow, but its easier. :)
jheriko
yes, I'm surprised to see how many voted for the Log solution (not that it's a terrible one)
Yeah, that's a good point to raise, one that I believe many ignored. And welcome to the "commenting club"! ;-)
Cerebrus
hah.. thanks Cerebrus ;)