如何在C中正确输出16位以上的整数?

时间:2016-12-11 11:14:29

标签: c factorial

此处的代码仅提供正确的输出,直到21的阶乘,之后,从左边开始正确的最多16位,然后剩余的数字仅为零。我尝试将变量c的类型从double更改为long double,但它只是出错或者不打印阶乘。

#include <stdio.h>

FILE *fp;
long double facto(int i);
int main() {
    int n, i;
    double c;
    printf("enter no. to find factorial till:\n");
    scanf("%d", &n);
    fp = fopen("output_of_factorial.txt", "w");
    fputs("Number  |\t Factorial\n\n", fp);

    for (i = 1; i <= n; i++) {
        c = facto(i);
        fprintf(fp, "%d\t|\t %.0Lf\n", i, c);
    }
    fclose(fp);
    return 0;
}

long double facto(int x) {
    if (x == 1)
        return 1;
    else
        return x * facto(x - 1);
}

3 个答案:

答案 0 :(得分:4)

Tye double只有53位精度,long double可能在您的平台上有80位。使用浮点算术将为您提供对于最高有效数字正确的近似结果。使用整数可以得到确切的结果,但前提是它小于类型的范围。

您可以使用至少64位宽的类型long long,即19位数字,或者再多一位,键入unsigned long long,允许整数增加两倍:

LLONG_MAX = 9223372036854775807  //  > 9.22e19
ULLONG_MAX = 18446744073709551615  // > 1.84e20

以下是代码的修改版本:

#include <stdio.h>

unsigned long long facto(int i);

int main(void) {
    int n, i;
    unsigned long long c;
    FILE *fp;

    printf("enter no. to find factorial till: ");
    if (scanf("%d", &n) == 1) {
        fp = fopen("output_of_factorial.txt", "w");
        if (fp != NULL) {
            fputs("Number |            Factorial\n\n", fp);
            for (i = 1; i <= n; i++) {
                c = facto(i);
                fprintf(fp, "%6d | %20llu\n", i, c);
            }
            fclose(fp);
        }
    }
    return 0;
}

unsigned long long facto(int x) {
    if (x <= 1)
        return 1;
    else
        return x * facto(x - 1);
}

它一直适用于20

Number |            Factorial

     1 |                    1
     2 |                    2
     3 |                    6
     4 |                   24
     5 |                  120
     6 |                  720
     7 |                 5040
     8 |                40320
     9 |               362880
    10 |              3628800
    11 |             39916800
    12 |            479001600
    13 |           6227020800
    14 |          87178291200
    15 |        1307674368000
    16 |       20922789888000
    17 |      355687428096000
    18 |     6402373705728000
    19 |   121645100408832000
    20 |  2432902008176640000

但由于算术溢出而导致21及以上失败。

更进一步,如果您的平台上可以使用128位整数(uint128_t__uint128__uint128_t),则可以使用128位整数,但您需要编写自己的转换函数输出十进制表示。

更好的方法是使用可以处理极大数字的多精度(又名 bignum )包,通常只受可用内存的限制。

答案 1 :(得分:1)

C通常使用机器字大小进行整数存储。使用阶乘,您最终会快速耗尽存储位。在某些时候,您需要将结果存储在一个数组和帐户中。汇编可能会有所帮助(因为你可能会发现乘法的乘法),或者你也可以使用整数的上半部分来编写乘法算法,类似于你在学校里教授的乘法算法。

如果所有这些听起来都很乏味,那么您可以使用任意精度库。

就这样你知道 long long在许多平台上都是64位,gcc在某些平台上支持int_128。然而,它会给有限的mialge提供因子。

答案 2 :(得分:1)

为了便于说明,这里是 bignum 的简单实现,我刚用可打印的内部作为ASCII字符串编写:

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

typedef struct bignum {
    const char *number;  // pointer to the printable representation
    char *array;         // array of size+1 byte for the digits
    size_t size;         // maximum number of significant digits
    size_t offset;       // offset if the first significant digit
    char buf[4 * sizeof(size_t)];
} bignum;

void bignum_free(bignum *bp) {
    if (bp && bp->array != bp->buf) {
        free(bp->array);
        bp->array = bp->buf;
    }
}

size_t bignum_realloc(bignum *bp) {
    size_t extra = bp->size / 2 + bp->size / 8;  // use pseudo Fibonacci sequence
    char *array, *source = bp->array;
    if (bp->array == bp->buf) {
        array = malloc(bp->size + extra);
    } else {
        source = array = realloc(bp->array, bp->size + extra);
    }
    if (array == NULL)
        return 0;
    memmove(array + extra, source, bp->size + 1);
    bp->array = array;
    bp->offset += extra;
    bp->size += extra;
    return extra;
}

bignum *bignum_normalize(bignum *bp) {
    while (bp->offset < bp->size && bp->array[bp->offset] == '0') {
        bp->offset++;
    }
    bp->number = bp->array + bp->offset;
    if (bp->offset >= bp->size) {
        bp->number = (bp->offset == bp->size) ? "0" : "overflow";
    }
    return  bp;
}

bignum *bignum_set_overflow(bignum *bp) {
    bp->offset = bp->size + 1;
    return bignum_normalize(bp);
}

bignum *bignum_init(bignum *bp) {
    size_t size = sizeof(bp->buf) - 1;
    bp->array = bp->buf;
    bp->offset = bp->size = size;
    bp->array[size] = '\0';
    return bignum_normalize(bp);
}

bignum *bignum_set_string(bignum *bp, const char *value) {
    bp->offset = bp->size;  // default value is 0
    if (value) {
        while (*value == '0') {
            value++;
        }
        size_t len = strlen(value);
        while (len > bp->size && bignum_realloc(bp))
            continue;
        if (len > bp->size) {
            bp->offset = bp->size + 1;  // overflow
        } else {
            bp->offset = bp->size - len;
            memcpy(bp->array + bp->offset, value, len);
        }
    }
    return bignum_normalize(bp);
}

bignum *bignum_init_string(bignum *bp, const char *value) {
    return bignum_set_string(bignum_init(bp), value);
}

bignum *bignum_add_uint(bignum *bp, unsigned int num) {
    unsigned long long carry = num;

    if (bp->offset > bp->size || num == 0) {
        return bp;
    }
    for (size_t i = bp->size;;) {
        if (carry == 0) {
            if (i < bp->offset) {
                bp->offset = i;
            }
            break;
        }
        if (i == 0) {
            bp->offset = 0;
            i += bignum_realloc(bp);
            if (i == 0) {
                bp->offset = bp->size + 1;  // overflow
                break;
            }
        }
        i--;
        if (i >= bp->offset) {
            carry += bp->array[i] - '0';
        }
        bp->array[i] = '0' + carry % 10;
        carry /= 10;
    }
    return bignum_normalize(bp);
}

bignum *bignum_set_value(bignum *bp, unsigned int value) {
    bp->offset = bp->size;
    return bignum_add_uint(bp, value);
}

bignum *bignum_init_value(bignum *bp, unsigned int value) {
    return bignum_add_uint(bignum_init(bp), value);
}

bignum *bignum_mul_uint(bignum *bp, unsigned int num) {
    unsigned long long carry = 0;

    if (bp->offset > bp->size || num == 1) {
        return bp;
    }
    if (num == 0) {
        bp->offset = bp->size;  /* special case 0 for speed */
    } else {
        for (size_t i = bp->size;;) {
            if (i == 0) {
                bp->offset = 0;
                if (carry == 0) {
                    break;
                }
                i += bignum_realloc(bp);
                if (i == 0) {
                    bp->offset = bp->size + 1;  // overflow
                    break;
                }
            }
            i--;
            if (i >= bp->offset) {
                carry += (unsigned long long)num * (bp->array[i] - '0');
            } else {
                if (carry == 0) {
                    bp->offset = i + 1;
                    break;
                }
            }
            bp->array[i] = '0' + carry % 10;
            carry /= 10;
        }
    }
    return bignum_normalize(bp);
}

int main(int argc, char *argv[]) {
    unsigned int n0 = 1, n1 = 100, max_digits = -1;
    bignum c;

    if (argc > 1) {
        n0 = n1 = strtol(argv[1], NULL, 0);
        if (argc > 2) {
            n1 = strtol(argv[2], NULL, 0);
            if (argc > 3) {
                max_digits = strtol(argv[3], NULL, 0);
            }
        }
        if (n1 < n0) {
            max_digits = n1;
            n1 = n0;
        }
    }

    bignum_init_value(&c, 1);
    printf("%6s | %s\n", "Number", "Factorial");
    for (unsigned int i = 1; i <= n1; i++) {
        bignum_mul_uint(&c, i);
        if (i >= n0) {
            if (c.size - c.offset > max_digits) {
                printf("%6u | %.1s.%.*se%zu\n",
                       i, c.number, max_digits - 1, c.number + 1,
                       c.size - c.offset - 1);
            } else {
                printf("%6u | %s\n", i, c.number);
            }
        }
    }
    bignum_free(&c);
    return 0;
}

您可以在命令行上传递参数:

chqrlie@macbook ~/dev/stackoverflow > ./factomp 1 60

Number | Factorial
     1 | 1
     2 | 2
     3 | 6
     4 | 24
     5 | 120
     6 | 720
     7 | 5040
     8 | 40320
     9 | 362880
    10 | 3628800
    11 | 39916800
    12 | 479001600
    13 | 6227020800
    14 | 87178291200
    15 | 1307674368000
    16 | 20922789888000
    17 | 355687428096000
    18 | 6402373705728000
    19 | 121645100408832000
    20 | 2432902008176640000
    21 | 51090942171709440000
    22 | 1124000727777607680000
    23 | 25852016738884976640000
    24 | 620448401733239439360000
    25 | 15511210043330985984000000
    26 | 403291461126605635584000000
    27 | 10888869450418352160768000000
    28 | 304888344611713860501504000000
    29 | 8841761993739701954543616000000
    30 | 265252859812191058636308480000000
    31 | 8222838654177922817725562880000000
    32 | 263130836933693530167218012160000000
    33 | 8683317618811886495518194401280000000
    34 | 295232799039604140847618609643520000000
    35 | 10333147966386144929666651337523200000000
    36 | 371993326789901217467999448150835200000000
    37 | 13763753091226345046315979581580902400000000
    38 | 523022617466601111760007224100074291200000000
    39 | 20397882081197443358640281739902897356800000000
    40 | 815915283247897734345611269596115894272000000000
    41 | 33452526613163807108170062053440751665152000000000
    42 | 1405006117752879898543142606244511569936384000000000
    43 | 60415263063373835637355132068513997507264512000000000
    44 | 2658271574788448768043625811014615890319638528000000000
    45 | 119622220865480194561963161495657715064383733760000000000
    46 | 5502622159812088949850305428800254892961651752960000000000
    47 | 258623241511168180642964355153611979969197632389120000000000
    48 | 12413915592536072670862289047373375038521486354677760000000000
    49 | 608281864034267560872252163321295376887552831379210240000000000
    50 | 30414093201713378043612608166064768844377641568960512000000000000
    51 | 1551118753287382280224243016469303211063259720016986112000000000000
    52 | 80658175170943878571660636856403766975289505440883277824000000000000
    53 | 4274883284060025564298013753389399649690343788366813724672000000000000
    54 | 230843697339241380472092742683027581083278564571807941132288000000000000
    55 | 12696403353658275925965100847566516959580321051449436762275840000000000000
    56 | 710998587804863451854045647463724949736497978881168458687447040000000000000
    57 | 40526919504877216755680601905432322134980384796226602145184481280000000000000
    58 | 2350561331282878571829474910515074683828862318181142924420699914240000000000000
    59 | 138683118545689835737939019720389406345902876772687432540821294940160000000000000
    60 | 8320987112741390144276341183223364380754172606361245952449277696409600000000000000

程序仅受可用内存的限制,但计算阶乘(100000)需要54秒,这是一个456574十进制数的数字。您可以将限制传递给位数:

chqrlie@macbook ~/dev/stackoverflow > ./factomp 100000 60

Number | Factorial
100000 | 2.82422940796034787429342157802453551847749492609122485057891e456573