以十进制形式打印大数字

时间:2011-10-14 14:03:54

标签: c algorithm numbers printf gmp

虽然数字的表示在某种程度上是相对的方面,但我们 通常在打印到外部世界时使用小数形式。

我在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这样的图书馆究竟如何处理这些?看起来像模数和 对于如此庞大的数字,分歧可能很昂贵。有没有更快的算法, 或者我错了,计算机的标准流程很容易吗?

1 个答案:

答案 0 :(得分:3)

标准过程并不容易,但是您需要通过这种或那种方式进行等效操作以获得十进制数字,这可能涉及高精度算术,即使二进制中的原始值只是几位或者单位。看到我的问题:

How do you print the EXACT value of a floating point number?

这是浮点数,但无论如何所有大的浮点数都是整数,非常大且非常小的情况是唯一有趣的情况。