虽然数字的表示在某种程度上是相对的方面,但我们 通常在打印到外部世界时使用小数形式。
我在Mac OS X中,在分析libc的来源时我发现了
着名的printf
函数最终调用了一个小函数__ultoa
- 之后
通过vfprintf_l
,1104行__vfprintf
,最后__ultoa
。
它的定义如下(在这种情况下,所有这些都直接来自FreeBSD):
/*
* Convert an unsigned long to ASCII for printf purposes, returning
* a pointer to the first character of the string representation.
* Octal numbers can be forced to have a leading zero; hex numbers
* use the given digits.
*/
static CHAR *
__ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs)
{
CHAR *cp = endp;
long sval;
/*
* Handle the three cases separately, in the hope of getting
* better/faster code.
*/
switch (base) {
case 10:
if (val < 10) { /* many numbers are 1 digit */
*--cp = to_char(val);
return (cp);
}
/*
* On many machines, unsigned arithmetic is harder than
* signed arithmetic, so we do at most one unsigned mod and
* divide; this is sufficient to reduce the range of
* the incoming value to where signed arithmetic works.
*/
if (val > LONG_MAX) {
*--cp = to_char(val % 10);
sval = val / 10;
} else
sval = val;
do {
*--cp = to_char(sval % 10);
sval /= 10;
} while (sval != 0);
break;
case 8:
do {
*--cp = to_char(val & 7);
val >>= 3;
} while (val);
if (octzero && *cp != '0')
*--cp = '0';
break;
case 16:
do {
*--cp = xdigs[val & 15];
val >>= 4;
} while (val);
break;
default: /* oops */
LIBC_ABORT("__ultoa: invalid base=%d", base);
}
return (cp);
}
此处CHAR
只是char
的类型定义(出于某种原因),to_char
基本上你期望的是:
#define to_char(n) ((n) + '0')
小数形式的转换以简单的方式进行,除以 10并采取%10:
do {
*--cp = to_char(sval % 10);
sval /= 10;
} while (sval != 0);
然而,虽然这个小数字(最多8个字节)的工作似乎太多了 “体力劳动”对我来说。在GMP中,您可以轻松计算2 5000 :
mpz_t n;
mpz_init(n);
mpz_ui_pow_ui(n, 2ul, 5000ul);
gmp_printf("%Zd\n", n);
虽然这对于基数2或16很容易表示,但十进制形式是a 有点难以计算。
那么,像GMP这样的图书馆究竟如何处理这些?看起来像模数和 对于如此庞大的数字,分歧可能很昂贵。有没有更快的算法, 或者我错了,计算机的标准流程很容易吗?
答案 0 :(得分:3)
标准过程并不容易,但是您需要通过这种或那种方式进行等效操作以获得十进制数字,这可能涉及高精度算术,即使二进制中的原始值只是几位或者单位。看到我的问题:
How do you print the EXACT value of a floating point number?
这是浮点数,但无论如何所有大的浮点数都是整数,非常大且非常小的情况是唯一有趣的情况。