我尝试使用以下代码片段在 64 位系统上计算阶乘:
long double FactorialLD(long n)
{
long double result = 1;
if (n <= 0)
return 1;
for (long i = 2; i <= n; i++)
result *= i;
return result;
}
30!
返回265252859812191058647452510846976
。但是,使用桌面计算器或https://www.calculatorsoup.com/calculators/discretemathematics/factorials.php返回265252859812191058636308480000000
。为什么会有这么大的差异,是什么原因造成的?
CPU:英特尔x86_64
操作系统:Ubuntu 24.10
编译器:g++ 14.2.0
在 x86-64 Linux 上使用 gcc 时,
long double
是80 位扩展精度格式。它具有 64 位有效数字,不足以准确表示30!
,其值略小于 2^108。(如果你检查的话,
sizeof(long double)
你会发现它是 16,但这只是为了对齐的原因。实际值仅使用 80 位,而其他 48 位是未使用的填充。)如果您确实拥有真正的128 位四倍精度,其有效数字为 113 位(计算隐含的前导 1 位),那么您确实能够通过这种方式获得 30! 的精确值。例如,Linux 上的 ARM64 使用四倍精度
long double
:在 godbolt 上尝试。(x86-64 上的 gcc 的当前版本实际上确实支持 128 位四精度算术,不是通过
long double
而是通过新_Float128
类型。 试试 godbolt。但是,仍然缺少标准库支持;例如,您还不能_Float128
使用printf
、iostream::operator<<
等输入或输出。)但请注意,目前没有主流 CPU/FPU 具有对 128 位四精度的硬件支持,因此该类型的计算都是在软件中完成的,并且相对较慢。(就此而言,x86 是唯一支持 80 位的主流平台,并且仅在其传统的 x87 指令集中,因此它比可以使用更现代的 SSE* 指令完成的单精度(32 位)或双精度(64 位)要慢一些。)