ANSI-C:打印十进制int的最大字符数

时间:2012-05-10 14:29:32

标签: c string int type-conversion

我想知道是否可以轻松确定打印小数int的最大字符数。

我知道<limits.h>包含像INT_MAX这样的定义,它们表示int可以假设的最大,但这不是我想要的。

我希望能够做到这样的事情:

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

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

但是如何以便携和低调的方式找到MAX_CHAR_OF_A_DECIMAL_INT的值?

谢谢!

7 个答案:

答案 0 :(得分:11)

如果您假设CHAR_BIT为8(对于任何针对POSIX系统的代码以及Windows等任何其他主流系统都是安全的假设,那么安全的假设是3*sizeof(int)+2。如果没有,您可以将其设为3*sizeof(int)*CHAR_BIT/8+2,或者版本稍微简单。

如果您对其工作原因感兴趣,sizeof(int)本质上是INT_MAX的对数(大致记录基数2 ^ CHAR_BIT),以及不同基数的对数之间的转换(例如,基数) 10)只是乘法。特别地,3是对数基数10为256的整数近似/上限。

+2是考虑可能的符号和空终止。

答案 1 :(得分:4)

最简单的规范和可以说是最便携的方式是问snprintf()需要多少空间:

char sbuf[2];
int ndigits;

ndigits = snprintf(sbuf, (size_t) 1, "%lld", (long long) INT_MIN);

可能使用intmax_t%j

稍微不那么便携
ndigits = snprintf(sbuf, (size_t) 1, "%j", (intmax_t) INT_MIN);

可以认为在运行时这样做太昂贵,但它可以适用于任何值,而不仅仅是任何整数类型的MIN / MAX值。

当然,您也可以直接计算给定整数需要用基本10表示法表示的位数,并使用简单的递归函数:

unsigned int
numCharsB10(intmax_t n)
{
        if (n < 0)
                return numCharsB10((n == INTMAX_MIN) ? INTMAX_MAX : -n) + 1;
        if (n < 10)
                return 1;

        return 1 + numCharsB10(n / 10);
}

但当然这也要求CPU在运行时,即使内联时也是如此,尽管可能比snprintf()稍微小一些。

@ R.上面的答案虽然或多或少都错了,但是在正确的轨道上。以下是使用sizeof()在编译时实现计算的一些非常好且经过广泛测试和高度可移植的宏的正确推导,使用@ R.的初始措辞的略微修正开始:

首先,我们可以很容易地看到(或显示)sizeof(int)UINT_MAX的对数基数2除以sizeof()的一个单位所代表的位数(8,aka { {1}}):

sizeof(int)== log2(UINT_MAX)/ 8

因为CHAR_BIT当然只是2 ^(sizeof(int)* 8))而log2(x)是2 ^ x的倒数。

我们可以使用标识“logb(x)= log(x)/ log(b)”(其中log()是自然对数)来找到其他碱基的对数。例如,您可以使用以下方法计算“x”的“log base 2”:

log2(x)= log(x)/ log(2)

还有:

log10(x)= log(x)/ log(10)

所以,我们可以推断出:

log10(v)= log2(v)/ log2(10)

现在我们想要的是UINT_MAX的日志基数10,所以因为log2(10)大约是3,因为我们从上面知道log2()是{{1} },我们可以说log10(UINT_MAX)大约是:

log10(2 ^(sizeof(int)* 8))〜=(sizeof(int)* 8)/ 3

虽然这并不完美,特别是因为我们真正想要的是天花板价值,但是通过一些微小的调整来解释log2(10)到3的整数舍入,我们可以通过首先添加一个来获得我们需要的东西。 log2 term,然后从任何大型整数的结果中减去1,得到这个“足够好”的表达式:

sizeof()

更好的是我们可以将我们的第一个log2()项乘以1 / log2(10)(乘以除数的倒数与除数除以相同),这样做可以找到更好的整数近似。我最近(重新?)在读Sean Anderson的时候遇到了这个建议:http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10

要使用整数运算来实现最佳逼近,我们需要找到代表倒数的理想比率。这可以通过搜索在2的幂的一些合理范围内乘以2的连续幂2的期望值1 / log2(10)的最小小数部分来找到,例如使用以下小AWK脚本:

UINT_MAX

因此我们可以得到一个很好的整数近似值,将log2(v)值乘以1 / log2(10)乘以1233,然后右移12(2 ^ 12当然是4096):< / p>

log10(UINT_MAX)〜=((sizeof(int)* 8)+ 1)* 1233&gt;&gt; 12

并且,除了添加一个相当于找到上限值之外,还可以摆脱使用奇数值的需要:

#if 0
#define __MAX_B10STRLEN_FOR_UNSIGNED_TYPE(t) \
    ((((sizeof(t) * CHAR_BIT) + 1) / 3) - ((sizeof(t) > 2) ? 1 : 0))
#endif

通常编译器会在编译时评估my awk 'BEGIN { minf=1.0 } END { for (i = 1; i <= 31; i++) { a = 1.0 / (log(10) / log(2)) * 2^i if (a > (2^32 / 32)) break; n = int(a) f = a - (n * 1.0) if (f < minf) { minf = f minn = n bits = i } # printf("a=%f, n=%d, f=%f, i=%d\n", a, n, f, i) } printf("%d + %f / %d, bits=%d\n", minn, minf, 2^bits, bits) }' < /dev/null 1233 + 0.018862 / 4096, bits=12 宏的表达式。当然,我的宏总是计算给定类型的整数所需的最大空间,而不是特定整数值所需的确切空间。

答案 2 :(得分:3)

b 位的有符号或无符号整数 x 的最大十进制位数 d 与该数字的十进制位数匹配2 ^ b 。 对于带符号的数字,必须为该符号添加一个额外的字符。

x 的小数位数可以计算为log_10( x ),四舍五入。

因此, x 的最大十进制数字为log_10(2 ^ b )= b * log_10(2)= b * 0.301029995663981,四舍五入。

如果 s 是用于存储 x 的某种整数类型的字节大小(由 sizeof 运算符给出),则其 b 的大小为 b = s *8。因此,最大十进制数字 d 将为是( s * 8)* 0.301029995663981,四舍五入。 舍入将包括截断(转换为整数)并加1。

当然,所有这些常量都必须加1以计算最后的0个字节(请参见下面的示例中的IntegerString)。

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#define COMMON_LOG_OF_2 0.301029995663981

#define MAX_DECIMAL_DIGITS_UCHAR        ((unsigned) (sizeof (unsigned char     ) * 8 * COMMON_LOG_OF_2) + 1)
#define MAX_DECIMAL_DIGITS_USHORT       ((unsigned) (sizeof (unsigned short    ) * 8 * COMMON_LOG_OF_2) + 1)
#define MAX_DECIMAL_DIGITS_UINT         ((unsigned) (sizeof (unsigned int      ) * 8 * COMMON_LOG_OF_2) + 1)
#define MAX_DECIMAL_DIGITS_ULONG        ((unsigned) (sizeof (unsigned long     ) * 8 * COMMON_LOG_OF_2) + 1)
#define MAX_DECIMAL_DIGITS_ULONGLONG    ((unsigned) (sizeof (unsigned long long) * 8 * COMMON_LOG_OF_2) + 1)
#define MAX_DECIMAL_DIGITS_UINT128      ((unsigned) (sizeof (unsigned __int128 ) * 8 * COMMON_LOG_OF_2) + 1)

#define MAX_DECIMAL_DIGITS_CHAR         (1 + MAX_DECIMAL_DIGITS_UCHAR    )
#define MAX_DECIMAL_DIGITS_SHORT        (1 + MAX_DECIMAL_DIGITS_USHORT   )
#define MAX_DECIMAL_DIGITS_INT          (1 + MAX_DECIMAL_DIGITS_UINT     )
#define MAX_DECIMAL_DIGITS_LONG         (1 + MAX_DECIMAL_DIGITS_ULONG    )
#define MAX_DECIMAL_DIGITS_LONGLONG     (1 + MAX_DECIMAL_DIGITS_ULONGLONG)
#define MAX_DECIMAL_DIGITS_INT128       (1 + MAX_DECIMAL_DIGITS_UINT128  )

int main (void)
{
    char IntegerString[MAX_DECIMAL_DIGITS_INT + 1];

    printf ("MAX_DECIMAL_DIGITS_UCHAR     = %2u\n",MAX_DECIMAL_DIGITS_UCHAR    );
    printf ("MAX_DECIMAL_DIGITS_USHORT    = %2u\n",MAX_DECIMAL_DIGITS_USHORT   );
    printf ("MAX_DECIMAL_DIGITS_UINT      = %2u\n",MAX_DECIMAL_DIGITS_UINT     );
    printf ("MAX_DECIMAL_DIGITS_ULONG     = %2u\n",MAX_DECIMAL_DIGITS_ULONG    );
    printf ("MAX_DECIMAL_DIGITS_ULONGLONG = %2u\n",MAX_DECIMAL_DIGITS_ULONGLONG);
    printf ("MAX_DECIMAL_DIGITS_UINT128   = %2u\n",MAX_DECIMAL_DIGITS_UINT128  );

    printf ("MAX_DECIMAL_DIGITS_CHAR      = %2u\n",MAX_DECIMAL_DIGITS_CHAR     );
    printf ("MAX_DECIMAL_DIGITS_SHORT     = %2u\n",MAX_DECIMAL_DIGITS_SHORT    );
    printf ("MAX_DECIMAL_DIGITS_INT       = %2u\n",MAX_DECIMAL_DIGITS_INT      );
    printf ("MAX_DECIMAL_DIGITS_LONG      = %2u\n",MAX_DECIMAL_DIGITS_LONG     );
    printf ("MAX_DECIMAL_DIGITS_LONGLONG  = %2u\n",MAX_DECIMAL_DIGITS_LONGLONG );
    printf ("MAX_DECIMAL_DIGITS_INT128    = %2u\n",MAX_DECIMAL_DIGITS_INT128   );

    sprintf (IntegerString,"%d",INT_MAX);
    printf ("INT_MAX       = %d\n",INT_MAX);
    printf ("IntegerString = %s\n",IntegerString);

    sprintf (IntegerString,"%d",INT_MIN);
    printf ("INT_MIN       = %d\n",INT_MIN);
    printf ("IntegerString = %s\n",IntegerString);

    return EXIT_SUCCESS;
}

编辑:

不幸的是,在将表达式作为常量求值时,使用浮点可能会引起问题。我已经通过乘以2 ^ 11并除以2 ^ 8来修改它们,以便所有计算都应由预处理器使用整数进行:

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#define LOG2_x_2_11 616 // log(2) * 2^11

#define MAX_DECIMAL_DIGITS_UCHAR        (((sizeof (unsigned char     ) * LOG2_x_2_11) >> 8) + 1)
#define MAX_DECIMAL_DIGITS_USHORT       (((sizeof (unsigned short    ) * LOG2_x_2_11) >> 8) + 1)
#define MAX_DECIMAL_DIGITS_UINT         (((sizeof (unsigned int      ) * LOG2_x_2_11) >> 8) + 1)
#define MAX_DECIMAL_DIGITS_ULONG        (((sizeof (unsigned long     ) * LOG2_x_2_11) >> 8) + 1)
#define MAX_DECIMAL_DIGITS_ULONGLONG    (((sizeof (unsigned long long) * LOG2_x_2_11) >> 8) + 1)
#define MAX_DECIMAL_DIGITS_UINT128      (((sizeof (unsigned __int128 ) * LOG2_x_2_11) >> 8) + 1)

#define MAX_DECIMAL_DIGITS_CHAR     (1 + MAX_DECIMAL_DIGITS_UCHAR    )
#define MAX_DECIMAL_DIGITS_SHORT    (1 + MAX_DECIMAL_DIGITS_USHORT   )
#define MAX_DECIMAL_DIGITS_INT      (1 + MAX_DECIMAL_DIGITS_UINT     )
#define MAX_DECIMAL_DIGITS_LONG     (1 + MAX_DECIMAL_DIGITS_ULONG    )
#define MAX_DECIMAL_DIGITS_LONGLONG (1 + MAX_DECIMAL_DIGITS_ULONGLONG)
#define MAX_DECIMAL_DIGITS_INT128   (1 + MAX_DECIMAL_DIGITS_UINT128  )

int main (void)
{
    char IntegerString[MAX_DECIMAL_DIGITS_INT + 1];

    printf ("MAX_DECIMAL_DIGITS_UCHAR     = %2zu\n",MAX_DECIMAL_DIGITS_UCHAR    );
    printf ("MAX_DECIMAL_DIGITS_USHORT    = %2zu\n",MAX_DECIMAL_DIGITS_USHORT   );
    printf ("MAX_DECIMAL_DIGITS_UINT      = %2zu\n",MAX_DECIMAL_DIGITS_UINT     );
    printf ("MAX_DECIMAL_DIGITS_ULONG     = %2zu\n",MAX_DECIMAL_DIGITS_ULONG    );
    printf ("MAX_DECIMAL_DIGITS_ULONGLONG = %2zu\n",MAX_DECIMAL_DIGITS_ULONGLONG);
    printf ("MAX_DECIMAL_DIGITS_UINT128   = %2zu\n",MAX_DECIMAL_DIGITS_UINT128  );

    printf ("MAX_DECIMAL_DIGITS_CHAR      = %2zu\n",MAX_DECIMAL_DIGITS_CHAR     );
    printf ("MAX_DECIMAL_DIGITS_SHORT     = %2zu\n",MAX_DECIMAL_DIGITS_SHORT    );
    printf ("MAX_DECIMAL_DIGITS_INT       = %2zu\n",MAX_DECIMAL_DIGITS_INT      );
    printf ("MAX_DECIMAL_DIGITS_LONG      = %2zu\n",MAX_DECIMAL_DIGITS_LONG     );
    printf ("MAX_DECIMAL_DIGITS_LONGLONG  = %2zu\n",MAX_DECIMAL_DIGITS_LONGLONG );
    printf ("MAX_DECIMAL_DIGITS_INT128    = %2zu\n",MAX_DECIMAL_DIGITS_INT128   );

    sprintf (IntegerString,"%d",INT_MAX);
    printf ("INT_MAX       = %d\n",INT_MAX);
    printf ("IntegerString = %s\n",IntegerString);

    sprintf (IntegerString,"%d",INT_MIN);
    printf ("INT_MIN       = %d\n",INT_MIN);
    printf ("IntegerString = %s\n",IntegerString);

    return EXIT_SUCCESS;
}

答案 3 :(得分:2)

我不知道在纯ANSI-C中做任何你想要的技巧是什么,但在C ++中你可以轻松地使用模板元编程来做:

#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
    };
};

你可以从你的纯C代码中调用它来创建一个额外的C ++函数:

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

这具有ZERO执行时间开销并计算所需的确切空间。


您可以使用以下内容测试上述模板:

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;
}

输出结果为:

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.
  • 请注意std::numeric_limits< T >::digits10和MaxLen&lt;&lt; T,N&gt; :: StringLen,因为如果不能达到'9',前者不会考虑数字。 当然你可以使用它,如果在某些情况下你不在乎浪费一个字节,只需添加两个。

编辑:

有些人可能会发现奇怪,包括<climits>。 如果您可以使用C ++ 11,那么您将不需要它,并且将获得额外的简单性:

#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
    };
};

现在你可以使用

MaxLen< short >::StringLen

而不是

MaxLen< short, SHRT_MAX >::StringLen

好,不是吗?

答案 4 :(得分:2)

接受回答(2岁以上)后

以下分数10/33完全符合未填充int8_tint16_tint32_tint128_t的需求。 char只有1 int64_t个额外费用。对于int362_t以下的所有整数,完全或1。除此之外可能还有更多。

#include <limits.h>
#define MAX_CHAR_LEN_DECIMAL_INTEGER(type) (10*sizeof(type)*CHAR_BIT/33 + 2)
#define MAX_CHAR_SIZE_DECIMAL_INTEGER(type) (10*sizeof(type)*CHAR_BIT/33 + 3)

int get_int( void ) {
                                            //   + 1 for the \n of fgets()
  char draft[MAX_CHAR_SIZE_DECIMAL_INTEGER(long) + 1];  //**

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

对于终止fgets()

** char通常效果最佳'\n'

@R..类似,但分数更高。


在阅读用户输入时,建议使用大量的2x缓冲区。有时用户会添加空格,前导零等。

  char draft[2*(MAX_CHAR_SIZE_DECIMAL_INTEGER(long) + 1)];
  fgets(draft, sizeof draft, stdin);

答案 5 :(得分:0)

这是C版:

#include <limits.h>

#define xstr(s) str(s)
#define str(s) #s
#define INT_STR_MAX sizeof(xstr(INT_MAX))

char buffer[INT_STR_MAX];

然后:

$ gcc -E -o str.cpp str.c
$ grep buffer str.cpp
char buffer[sizeof("2147483647")];

$ gcc -S -o str.S str.c
$ grep buffer str.S
    .comm   buffer,11,1

答案 6 :(得分:0)

在C ++ 11和更高版本中,您可以执行以下操作:

namespace details {
    template<typename T>
    constexpr size_t max_to_string_length_impl(T value) {
        return (value >= 0 && value < 10) ? 1                            // [0..9] -> 1
            : (std::is_signed<T>::value && value < 0 && value > -10) ? 2 // [-9..-1] -> 2
            : 1 + max_to_string_length_impl(value / 10);                 // ..-10] [10.. -> recursion
    }
}

template<typename T>
constexpr size_t max_to_string_length() { 
    return std::max(
        details::max_to_string_length_impl(std::numeric_limits<T>::max()),
        details::max_to_string_length_impl(std::numeric_limits<T>::min())); 
}