fljx fljx - 3 months ago 32
C Question

ANSI-C: maximum number of characters printing a decimal int

I'd like to know if it is an easy way of determining the maximum number of characters to print a decimal

int
.

I know
<limits.h>
contains definitions like
INT_MAX
that say the maximum value an int can assume, but it is not what I want.

I'd like to be able to do something like:

int get_int( void )
{
char draft[ MAX_CHAR_OF_A_DECIMAL_INT ];

fgets( draft, sizeof( draft ), stdin );
return strtol( draft, NULL, 10 );
}


But how to find the value of
MAX_CHAR_OF_A_DECIMAL_INT
in a portable and low overheaded way?

Thanks!

Answer

I don't know if it is any trick to do what you want in plain ANSI-C, but in C++ you can easily use template metaprogramming to do:

#include    <iostream>
#include    <limits>
#include    <climits>

template< typename T, unsigned long N = INT_MAX >
class   MaxLen
{
public:
    enum
    {
        StringLen = MaxLen< T, N / 10 >::StringLen + 1
    };
};

template< typename T >
class   MaxLen< T, 0 >
{
public:
    enum
    {
        StringLen = 1
    };
};

And you can call it from your pure-C code creating an additional C++ function like this:

extern "C"
int int_str_max( )
{
    return  MaxLen< int >::StringLen;
}

This has a ZERO execution time overhead and calculates the exact space needed.


You can test the above templates with something like:

int main( )
{
std::cout << "Max: " << std::numeric_limits< short >::max( ) << std::endl;
std::cout << "Digits: " << std::numeric_limits< short >::digits10 << std::endl;
std::cout << "A \"short\" is " << sizeof( short ) << " bytes." << std::endl
    << "A string large enough to fit any \"short\" is "
    << MaxLen< short, SHRT_MAX >::StringLen << " bytes wide." << std::endl;

std::cout << "Max: " << std::numeric_limits< int >::max( ) << std::endl;
std::cout << "Digits: " << std::numeric_limits< int >::digits10 << std::endl;
std::cout << "An \"int\" is " << sizeof( int ) << " bytes." << std::endl
    << "A string large enough to fit any \"int\" is "
    << MaxLen< int >::StringLen << " bytes wide." << std::endl;

std::cout << "Max: " << std::numeric_limits< long >::max( ) << std::endl;
std::cout << "Digits: " << std::numeric_limits< long >::digits10 << std::endl;
std::cout << "A \"long\" is " << sizeof( long ) << " bytes." << std::endl
    << "A string large enough to fit any \"long\" is "
    << MaxLen< long, LONG_MAX >::StringLen << " bytes wide." << std::endl;

    return  0;
}

The output is:

Max: 32767
Digits: 4
A "short" is 2 bytes.
A string large enough to fit any "short" is 6 bytes wide.
Max: 2147483647
Digits: 9
An "int" is 4 bytes.
A string large enough to fit any "int" is 11 bytes wide.
Max: 9223372036854775807
Digits: 18
A "long" is 8 bytes.
A string large enough to fit any "long" is 20 bytes wide.
  • Note the slightly different values from std::numeric_limits< T >::digits10 and MaxLen< T, N >::StringLen, as the former does not take into account digits if if can't reach '9'. Of course you can use it and simply add two if you don't care wasting a single byte in some cases.

EDIT:

Some may have found weird including <climits>. If you can count with C++11, you won't need it, and will earn an additional simplicity:

#include    <iostream>
#include    <limits>

template< typename T, unsigned long N = std::numeric_limits< T >::max( ) >
class   MaxLen
{
public:
    enum
    {
        StringLen = MaxLen< T, N / 10 >::StringLen + 1
    };
};

template< typename T >
class   MaxLen< T, 0 >
{
public:
    enum
    {
        StringLen = 1
    };
};

Now you can use

MaxLen< short >::StringLen

instead of

MaxLen< short, SHRT_MAX >::StringLen

Good, isn't?