最简单的规范和可以说是最便携的方法是询问snprintf()
需要多少空间:
char sbuf[2];
int ndigits;
ndigits = snprintf(sbuf, (size_t) 1, "%lld", (long long) INT_MIN);
稍微不那么便携可能使用intmax_t
and %j
:
ndigits = snprintf(sbuf, (size_t) 1, "%j", (intmax_t) INT_MIN);
人们可能会认为在运行时这样做太昂贵了,但它可以用于任何值,而不仅仅是任何整数类型的 MIN/MAX 值。
当然,您也可以使用简单的递归函数直接计算给定整数需要以 Base 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)
是以 2 为底UINT_MAX
的对数除以sizeof()
(8, aka 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”的“log base 2”:
log2(x) = log(x) / log(2)
并且:
log10(x) = log(x) / log(10)
所以,我们可以推断:
log10(v) = log2(v) / log2(10)
现在我们最终想要的是 的以 10 为底的对数UINT_MAX
,所以由于 log2(10) 大约是 3,并且由于我们从上面知道 log2() 是关于 的sizeof()
,所以我们可以说 log10( UINT_MAX
) 大约是:
log10(2^(sizeof(int)*8)) ~= (sizeof(int) * 8) / 3
虽然这并不完美,特别是因为我们真正想要的是上限值,但是通过一些小的调整来考虑 log2(10) 到 3 的整数舍入,我们可以通过首先在 log2 项上加一个来得到我们需要的东西,然后从任何较大整数的结果中减去 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)(乘以除数的倒数与除数相同),这样做可以找到更好的整数近似值。我最近(重新?)在阅读肖恩安德森的比特黑客时遇到了这个建议: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=%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
所以我们可以得到一个很好的整数近似值,将我们的 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()
宏变成的表达式。当然,我的宏总是计算给定类型整数所需的最大空间,而不是特定整数值所需的确切空间。