while 循环阶乘最多只能计算 20?

while 循环阶乘最多只能计算 20?

这个特定的 while 循环承认它只能工作到数字 20。之后,它开始给出错误的答案,但我不明白为什么。

#!/bin/bash

n=$1
[ "$n" == "" ] && echo Please give a number and try again && exit

factorial=1 ; j=1

while [ $j -le $n ]
do
    factorial=$(( $factorial * $j ))
    j=$(( $j + 1 ))
done
echo The factorial of $n, "$n"'!' = $factorial
exit 0

如果您给出 21 作为参数,您将得到:-4249290049419214848。可能是 bash 问题吗?我尝试进行下面的阶乘计算,但得到了同样错误的答案。答案应该是:51090942171709440000

echo $(( 2432902008176640000 * 21 ))

答案1

Bash 在大多数系统上使用带符号的 64 位整数。您将需要另一个类似的工具bc来超越这一点。

这意味着使用 bash 可以拥有的最大数量是2^63-1, 等于9,223,372,036,854,775,807并且您超过了这个数量:

awk 'BEGIN{print 2432902008176640000 * 21}'
51090942171709440000     #--> 51,090,942,171,709,440,000

答案2

就像doneal24所说,你可能需要离开bc

将以下内容添加到名为fact.bc 的文件中:

define f (x) {
  if (x <= 1) return (1);
  return (f(x-1) * x);
}

现在您可以通过提供文件在 bc 中调用它:例如

$>bc fact.bc <<< "scale=50;f(200)"
78865786736479050355236321393218506229513597768717326329474253324435\
94499634033429203042840119846239041772121389196388302576427902426371\
05061926624952829931113462857270763317237396988943922445621451664240\
25403329186413122742829485327752424240757390324032125740557956866022\
60319041703240623517008587961789222227896237038973747200000000000000\
00000000000000000000000000000000000

答案3

这就是所谓的整数溢出错误。根据 @doneal24 的回答,Bash 使用 64 位整数,但我将在解释中使用 8 位整数,以便更容易地显示正在发生的情况。

8 位整数在 8 位内存储正数或负数,但并非所有这些位都存储整数的值 - 数字的第一位,即符号位,表示该数字是正数还是负数(用“ 0”表示正,“1”表示负),接下来的七位使用标准二进制表示法组成相关数字的值。

例如,一个值为 1 的整数如下所示:

0000 0001

然后,我们可以开始运行您的程序,并通过将其乘以计算下一个阶乘所需的值来开始增加该整数的值。因此,如果我们将其乘以 2,然后开始重复该过程,我们将得到以下结果:

0000 0001×0000 0010 = 0000 0010

0000 0010×0000 0011 = 0000 1010

0000 1010×0000 0100 = 0001 1000

0001 1000×0000 0101 = 0111 1000

现在我们已经很接近了。下一次迭代是错误发生的地方:

0111 1000×0000 0110 = 0010 1101 0000

您可以注意到它溢出了分配给存储整数值的八位 - 这意味着它会覆盖存储在整数存储位置之前的内存位置中的任何内容,这是个坏消息,因为它可能导致计算机进程损坏;事实上,过去曾有黑客利用类似的安全漏洞来攻击计算机。

不过,你也可以看到整数所在位置存储的八位是“1101 0000”。这意味着,当您的计算机解释其值时,它将读取第一位作为符号位,表示负数,然后将数字的其余部分读取为“101 0000”或80,因此它会显示该操作的值为“-80”。

相关内容