views:

160

answers:

2

Given the maximum possible value, how to simply express the space needed to write such number in decimal form as text ?

The real task: logging process ids (pid_t) with fixed length, using gcc on Linux. It'd be good to have a compile time expression to be used in the std::setw() iomanipulator.

I have found that linux/threads.h header contains a PID_MAX value with the maximum pid allocated to a process. So having

#define LENGTH(t) sizeof(#t)-1

the LENGTH(PID_MAX) would be a compile time expression, but unfortunatelly this number is defined in hexa:

#define PID_MAX 0x8000

My current best solution is a bit oddish

static_cast<int>( ::floor( ::log(PID_MAX)/::log(10) + 1 ) );

But this is calculated runtime and uses functions from math.h

+2  A: 

I don't think you can get it exactly without invoking logarithms, but you can get an upper bound:

CHAR_BIT * sizeof(PID_MAX) will give you an upper bound on the number of bits needed to represent PID_MAX. You can then precompute log(10) = 3.32 and round down to 3. Forget about floor, because integer division will truncate like that anyhow. So

#define LENGTH(t) (((CHAR_BIT * sizeof(t)) / 3) + 1)

Should give you a compile-time computable upper bound on the number of characters needed to display t in decimal.

Tyler McHenry
This LENGTH(PID_MAX) would return 11 on a 64 bit OS, which is a bit too wide for 5 chars
CsTamas
Like I said, upper bound, not exact.
Tyler McHenry
OK. But the template version is more "elegant"
CsTamas
Yep, I upvoted it. Go with that.
Tyler McHenry
+13  A: 

You could do it with a little template meta programming:

//NunLength_interal does the actual calculation. 
template <unsigned num>
struct NumLength_internal
{ enum { value = 1 + NumLength_internal<num/10>::value }; };

template <>
struct NumLength_internal<0>
{ enum { value = 0 }; };

//NumLength is a wrapper to handle zero. For zero we want to return
//a length of one as a special case.
template <unsigned num>
struct NumLength
{ enum { value = NumLength_internal<num>::value };};

template <>
struct NumLength<0>
{ enum { value = 1 }; };

This should work for anything now. For example:

cout << NumLength<0>::value      << endl; // writes: 1
cout << NumLength<5>::value      << endl; // writes: 1
cout << NumLength<10>::value     << endl; // writes: 2
cout << NumLength<123>::value    << endl; // writes: 3
cout << NumLength<0x8000>::value << endl; // writes: 5

This is all handled at compile time.

Edit: I added another layer to handle the case when the number passed in is zero.

Matt Price
Arg, I was just going to post something almost exactly like that!
Zifre
While this is totally working, I shudder at the thought of my Preprocesor having to calculate this on some really high number.
AndreasT
@AndreasT: How high are we talking? 10^5? 10^20?
JAB
@AndreasT: It's the compiler that does the work. The problem is O( log( N ) ). No issue for even numeric_limits<__int64>::max(), which needs approximately 21 iterations. Besides, what else has the compiler to do?
xtofl
This is nothing, you should see some of stuff in Abrahams' book on the subject: http://www.boostpro.com/mplbook/ The compiler won't even flinch for this.
Matt Price