我可以使用long double来计算c语言中的整数吗

Can i use long double to compute integers in c language

我尝试写一个阶乘函数来计算一个大数(factorial(105)),它的结果有168位,所以使用long double,但它似乎是一个错误,不能这样使用吗?

#include <stdio.h>

long double factorial(long double n,long double base = 1){
  if (n <= 1){
    return 1*base;
  }else{
    return factorial(n-1,n * base);
  }
}  
int main(){  
    printf("%.0Lf\n",factorial(25));     // this result is correct

    printf("%.0Lf\n",factorial(26));    
    //correct result is 403291461126605635584000000,but it return 403291461126605635592388608
    return 0;
}

信封背面计算:25!略大于1025;三个数量级大约是 10 位,所以你需要大约 83 位的尾数,即使只是为了精确地表示结果。

鉴于long double,在支持它的平台上,一般是80位整个值(不仅仅是尾数!),显然没有办法您有足够的尾数以整数精度执行该数量级的计算。

然而:这里的阶乘有点神奇,因为很多因子都是2的幂,只是在右边加二进制零,不需要尾数(它们以指数结束)。特别是:

25! = 2   4   2   8   2    4    2    16    2    4     2    8    = 2²² · m
        3   5 3 7   9 5 11 3 13 7 15    17 9 19 5 21 11 23 3 25 

(m是所有非2因子的乘积,即m = 310 · 56 · 73 · 112 · 13 · 17 · 19 · 23,所以有效的数据我们必须存储在尾数中)

因此,我们的初步估算超出了实际要求22位。

原来是

log2(f) = 10·log23 + 6·log25 + 3·log27 + 2·log211 + log213 + log 217 + log219 + log223 = 61.68

确实刚好小于 80 位长双精度(64 位)尾数的大小。但是,当您将它乘以 26(因此,不包括以指数结尾的因子 2 乘以 13)时,您将添加 log2(13) = 3.7 位。 61.7+3.7就是65.4,所以从26开始!从那以后,您不再能够精确地执行计算。

首先,没有人知道long double能代表什么,不能代表什么。格式的具体属性取决于实现。

其次,X86扩展精度浮点格式有64位有效位,显式前导1,这意味着它可以表示±264中的连续整数范围。超出该范围可表示的整数是不连续的(即它们开始 "skip" 并且间隙越来越大)。您的阶乘远远超出该范围,这意味着完全可以预期它们不会被精确表示。

从 105 年开始!是一个 巨大的 数字,不适合单个单词(或其中两个),您需要 arbitrary precision arithmetic, also known as bignums. Use the Stirling's approximation to get an idea of how big 105! is and read the wikipage on factorials.

标准 C(阅读 n1570 进行检查)本身没有 bignums,但您会找到许多库。

我推荐GMPlib。顺便说一句,出于性能原因,它的一些代码是手写的汇编代码(在编写 bignum 加法时,你想利用 add with carry 机器指令)。

我建议避免编写自己的 bignum 操作。现有的图书馆使用非常聪明的算法(你需要做一些博士工作才能得到更好的东西)。如果您尝试编写自己的 bignum 库,它可能会比竞争对手差很多(除非您花费多年的工作)。