34

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.

Adam Siler
  • 1,986
  • 5
  • 22
  • 26
  • 2
    Though the logarithm solution received the most up votes, in my opinion the string conversion (which, surprisingly, was proposed only once) is the better one. It mirrors exactly your intent and there is a chance that you can write a universal component which does that for any kind of items (not just numbers). – Paul Michalik Jul 03 '12 at 17:40

10 Answers10

52

This should do it:

int length = (number ==0) ? 1 : (int)Math.log10(number) + 1;
Nicholas Mancuso
  • 11,599
  • 6
  • 45
  • 47
  • 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 Feb 16 '09 at 21:23
  • 1
    Last time I checked, the number zero has one digit, not zero. So shouldn't number==0 imply length = 1? – Rob Kennedy Feb 16 '09 at 22:31
  • 3
    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. – Adam Siler Feb 17 '09 at 03:28
12
int length = (int)Math.Log10(Math.Abs(number)) + 1;

You may need to account for the negative sign..

Ryan Emerle
  • 15,461
  • 8
  • 52
  • 69
12

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 = 10;
while(n < test[digits]) --digits;
Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
jheriko
  • 3,043
  • 1
  • 21
  • 28
  • I think this is the clearest, most efficient solution. However your array should start with 0, as 0 has 1 digit. – Rob Elliott Sep 23 '09 at 17:42
  • Practically, from a performance point of view the lookup table is the absolute best solution. The multipling is the second best solution as it takes less time than the log or the division versions. – Robert S. Barnes Nov 18 '12 at 10:29
  • I just realized there's a bug in the lookup version. If n is larger than or equal to 1000000000 ( 10 digits ) you will fall off the end of the array. – Robert S. Barnes Nov 19 '12 at 10:15
4

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
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 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 Feb 16 '09 at 21:04
  • 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 Feb 17 '09 at 03:56
  • @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 Feb 17 '09 at 04:32
  • indeed, in practice log might even be faster than what i suggested :) – jheriko Feb 17 '09 at 14:03
  • @jheriko div instructions are irrelevant, since no sane compiler generates them for divisions by a compile-time constant. https://godbolt.org/z/b7eW5YPYG – Craig Barnes Mar 22 '22 at 20:47
4

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

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

might do what you want.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
cjk
  • 45,739
  • 9
  • 81
  • 112
  • 1
    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 Feb 16 '09 at 22:33
2

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..

user67143
  • 317
  • 1
  • 4
  • thanks for the backup, but if the majority prefer the logarithm solution it is their choice. it may be slow, but its easier. :) – jheriko Feb 17 '09 at 00:31
  • yes, I'm surprised to see how many voted for the Log solution (not that it's a terrible one) – user67143 Feb 17 '09 at 01:11
  • Yeah, that's a good point to raise, one that I believe many ignored. And welcome to the "commenting club"! ;-) – Cerebrus Mar 01 '09 at 10:19
1

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
  • 123,280
  • 14
  • 225
  • 444
  • I like to explore alternative solutions. Sometimes I learn things - like log10. – Adam Siler Feb 17 '09 at 03:37
  • 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 Feb 17 '09 at 11:56
0

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

mouviciel
  • 66,855
  • 13
  • 106
  • 140
0

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++;
}
Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
achinda99
  • 5,020
  • 4
  • 34
  • 42
0

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
  • 110,348
  • 25
  • 193
  • 263