我想知道这是否是确定打印小数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
的价值呢?
谢谢!
如果你假设CHAR_BIT
是8(在POSIX上是必需的,所以对于任何针对POSIX系统以及任何其他主流系统(如Windows(的代码来说,这是一个安全的假设(,一个便宜的安全公式是3*sizeof(int)+2
。如果没有,您可以将其设为3*sizeof(int)*CHAR_BIT/8+2
,或者有一个稍微简单的版本。
如果您对这起作用的原因感兴趣,sizeof(int)
本质上是 INT_MAX
的对数(大致以对数为底数 2^CHAR_BIT(,不同基数的对数之间的转换(例如以 10 为底(只是乘法。特别是,3 是 256 的对数底数 10 上的整数近似/上限。
+2 用于考虑可能的符号和空终止。
规范和可以说是最便携的方法是询问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,又名 CHAR_BIT
(:
sizeof(int( == log2(UINT_MAX(/8
因为UINT_MAX
当然只是 2 ^ (sizeof(int( * 8((,而 log2(x( 是 2^x 的逆数。
我们可以使用恒等式"logb(x( = log(x(/log(b("(其中 log(( 是自然对数(来查找其他底数的对数。 例如,您可以使用以下命令计算"x"的"对数基数 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(( 在 sizeof()
方面是什么,我们可以说 log10(UINT_MAX
( 大约是:
log10(2^(sizeof(int(*8(( ~= (sizeof(int( * 8(/3
虽然这并不完美,特别是因为我们真正想要的是上限值,但是通过一些小的调整来考虑log2(10(到3的整数舍入,我们可以通过首先在log2项中添加1,然后从任何较大整数的结果中减去1来获得我们需要的东西,从而得到这个"足够好"的表达式:
#if 0
#define __MAX_B10STRLEN_FOR_UNSIGNED_TYPE(t)
((((sizeof(t) * CHAR_BIT) + 1) / 3) - ((sizeof(t) > 2) ? 1 : 0))
#endif
更好的是,我们可以将我们的第一个 log2(( 项乘以 1/log2(10((乘以除数的倒数与除数除数相同(,这样做可以找到更好的整数近似。 我最近(重新?在阅读肖恩·安德森(Sean Anderson(的bithacks时遇到了这个建议:http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
为了使用整数数学来达到最佳近似值,我们需要找到代表倒数的理想比率。 这可以通过搜索将我们期望的值 1/log2(10( 乘以 2 的连续幂的最小小数部分来找到,在 2 的某个合理的幂范围内,例如使用以下小 AWK 脚本:
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=%dn", a, n, f, i)
}
printf("%d + %f / %d, bits=%dn", minn, minf, 2^bits, bits)
}' < /dev/null
1233 + 0.018862 / 4096, bits=12
因此,我们可以得到一个很好的整数近似值,将我们的 log2(v( 值乘以 1/log2(10(,方法是将其乘以 1233,然后右移 12(当然 2^12 是 4096(:
log10(UINT_MAX( ~= ((sizeof(int( * 8( + 1( * 1233>> 12
而且,再加上添加一个相当于找到上限值,这就摆脱了摆弄奇数值的需要:
#define __MAX_B10STRLEN_FOR_UNSIGNED_TYPE(t)
(((((sizeof(t) * CHAR_BIT)) * 1233) >> 12) + 1)
/*
* for signed types we need room for the sign, except for int64_t
*/
#define __MAX_B10STRLEN_FOR_SIGNED_TYPE(t)
(__MAX_B10STRLEN_FOR_UNSIGNED_TYPE(t) + ((sizeof(t) == 8) ? 0 : 1))
/*
* NOTE: this gives a warning (for unsigned types of int and larger) saying
* "comparison of unsigned expression < 0 is always false", and of course it
* is, but that's what we want to know (if indeed type 't' is unsigned)!
*/
#define __MAX_B10STRLEN_FOR_INT_TYPE(t)
(((t) -1 < 0) ? __MAX_B10STRLEN_FOR_SIGNED_TYPE(t)
: __MAX_B10STRLEN_FOR_UNSIGNED_TYPE(t))
而通常编译器会在编译时计算我的__MAX_B10STRLEN_FOR_INT_TYPE()
宏变成的表达式。 当然,我的宏总是计算给定类型的整数所需的最大空间,而不是特定整数值所需的确切空间。
普通 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;
}
这具有零执行时间开销,并计算所需的确切空间。
您可以使用以下内容测试上述模板:
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<T、N>:StringLen 的值略有不同,因为如果 if 无法达到 '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
很好,不是吗?
有符号或无符号整数 x b 位的最大十进制数字 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 = %2un",MAX_DECIMAL_DIGITS_UCHAR );
printf ("MAX_DECIMAL_DIGITS_USHORT = %2un",MAX_DECIMAL_DIGITS_USHORT );
printf ("MAX_DECIMAL_DIGITS_UINT = %2un",MAX_DECIMAL_DIGITS_UINT );
printf ("MAX_DECIMAL_DIGITS_ULONG = %2un",MAX_DECIMAL_DIGITS_ULONG );
printf ("MAX_DECIMAL_DIGITS_ULONGLONG = %2un",MAX_DECIMAL_DIGITS_ULONGLONG);
printf ("MAX_DECIMAL_DIGITS_UINT128 = %2un",MAX_DECIMAL_DIGITS_UINT128 );
printf ("MAX_DECIMAL_DIGITS_CHAR = %2un",MAX_DECIMAL_DIGITS_CHAR );
printf ("MAX_DECIMAL_DIGITS_SHORT = %2un",MAX_DECIMAL_DIGITS_SHORT );
printf ("MAX_DECIMAL_DIGITS_INT = %2un",MAX_DECIMAL_DIGITS_INT );
printf ("MAX_DECIMAL_DIGITS_LONG = %2un",MAX_DECIMAL_DIGITS_LONG );
printf ("MAX_DECIMAL_DIGITS_LONGLONG = %2un",MAX_DECIMAL_DIGITS_LONGLONG );
printf ("MAX_DECIMAL_DIGITS_INT128 = %2un",MAX_DECIMAL_DIGITS_INT128 );
sprintf (IntegerString,"%d",INT_MAX);
printf ("INT_MAX = %dn",INT_MAX);
printf ("IntegerString = %sn",IntegerString);
sprintf (IntegerString,"%d",INT_MIN);
printf ("INT_MIN = %dn",INT_MIN);
printf ("IntegerString = %sn",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 = %2zun",MAX_DECIMAL_DIGITS_UCHAR );
printf ("MAX_DECIMAL_DIGITS_USHORT = %2zun",MAX_DECIMAL_DIGITS_USHORT );
printf ("MAX_DECIMAL_DIGITS_UINT = %2zun",MAX_DECIMAL_DIGITS_UINT );
printf ("MAX_DECIMAL_DIGITS_ULONG = %2zun",MAX_DECIMAL_DIGITS_ULONG );
printf ("MAX_DECIMAL_DIGITS_ULONGLONG = %2zun",MAX_DECIMAL_DIGITS_ULONGLONG);
printf ("MAX_DECIMAL_DIGITS_UINT128 = %2zun",MAX_DECIMAL_DIGITS_UINT128 );
printf ("MAX_DECIMAL_DIGITS_CHAR = %2zun",MAX_DECIMAL_DIGITS_CHAR );
printf ("MAX_DECIMAL_DIGITS_SHORT = %2zun",MAX_DECIMAL_DIGITS_SHORT );
printf ("MAX_DECIMAL_DIGITS_INT = %2zun",MAX_DECIMAL_DIGITS_INT );
printf ("MAX_DECIMAL_DIGITS_LONG = %2zun",MAX_DECIMAL_DIGITS_LONG );
printf ("MAX_DECIMAL_DIGITS_LONGLONG = %2zun",MAX_DECIMAL_DIGITS_LONGLONG );
printf ("MAX_DECIMAL_DIGITS_INT128 = %2zun",MAX_DECIMAL_DIGITS_INT128 );
sprintf (IntegerString,"%d",INT_MAX);
printf ("INT_MAX = %dn",INT_MAX);
printf ("IntegerString = %sn",IntegerString);
sprintf (IntegerString,"%d",INT_MIN);
printf ("INT_MIN = %dn",INT_MIN);
printf ("IntegerString = %sn",IntegerString);
return EXIT_SUCCESS;
}
接受答案后 (2+ 年(
下面的分数 10/33 正好满足无填充int8_t
、int16_t
、int32_t
和int128_t
的需要。 int64_t
只有1 char
额外费用。 对于所有整数大小,精确或超过 1 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()
通常最适合使用终止'n'
的附加char
。
与@R相似,但比例更高。
建议在读取用户输入时使用慷慨的 2x 缓冲区。 有时用户会添加空格、前导零等。
char draft[2*(MAX_CHAR_SIZE_DECIMAL_INTEGER(long) + 1)];
fgets(draft, sizeof draft, stdin);
在 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()));
}
您可以使用以对数为 10 来计算位数。在我的系统中,使用数字的位表示计算对数基数 2 的上限并没有提供任何显着的速度提升。对数基数 10 + 1 的地板给出了位数,我加 2 以考虑空字符和符号。
#include <limits.h>
#include <stdio.h>
#include <math.h>
int main(void){
printf("%d %dn", INT_MAX, (int)floor(log10(INT_MAX)) + 3);
return 0;
}
另请注意,int
的字节数可以是 2 或 4,仅在旧系统中是 2,因此您可以计算上限并在程序中使用它。
这是 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