公元前

公元前

我正在尝试编写一个脚本(script1.sh),给出第一个数字中每个数字的总和,并计算第二个数字的幂。所以

./script1.sh 12345 2

应该输出55

(因为1+4+9+16+25=55)

或者./script1.sh 3706907995955475988644381 25

应该输出3706907995955475988644381.

我编写了一个脚本,但在某些情况下我得到了负输出,但我不知道这是如何发生的。

例如

./script1.sh 3706907995955475988644380 25

输出

-2119144605827694052

我的脚本:

#!/bin/bash
sum=0

value=$1

arr=()
for ((i = 0; i < ${#value}; i++)); do
    arr+=(${value:$i:1})
done

for x in "${arr[@]}"; do
    sum=$(($sum+(x**$2)))
done
echo $sum

答案1

shell 算术 inbash使用 C 编译器支持的最宽整数类型。在大多数现代系统/C 编译器上,这是 64 位整数,因此“仅”覆盖范围 -9223372036854775808 到 9223372036854775807,并换行其中的数字。为此,您需要使用另一个工具,例如 bc:

#!/bin/bash

num1=$1
num2=$2
sum=0

for (( i=0; i<${#num1}; i++ )); do
    n=${num1:$i:1}
    sum=$( bc <<<"$sum + $(bc <<<"${n}^$num2")" )
done

echo "$sum"

答案2

带短awk脚本:

sum_powered.awk脚本:

#!/bin/awk -f
BEGIN{
    split(ARGV[1], a, "");
    pow = ARGV[2];
    for (i in a) sum += a[i] ** pow;
    print sum;
}

用法:

$ ./sum_powered.awk 12345 2
55

$ ./sum_powered.awk 3706907995955475988644381 25
3706907995955475217645568

您可能需要将执行权限添加到新的awk运行前的脚本:

$ chmod +x sum_powered.awk 

答案3

进行具有 25 位数字 ( 3706907995955475988644381) 结果的数学运算肯定超出了大多数 shell 实现的能力。事实上,shell算术与C语言算术非常相似。在C语言中,有符号整数在溢出时翻转符号。 64 位系统使用的常见最长系统整数的值限制为 63 个二进制数字(另一位定义整数的符号),因此,二进制中的 63 个 1 或十六进制中的 0efff ffff ffff ffff 表示数字 $( ( (1<<63) - 1 )) 或 9223372036854775807(19 位数字)。负限为-9223372036854775808。

25 位数字无法放入 19 位整数中,因此会溢出,如下所示C 有符号整数溢出:通过改变符号(在大多数机器上):

$ echo "$(( 9223372036854775807 + 1 ))"
-9223372036854775808

公元前

(独立)实用程序可以为“任意精度数学”(没有预设长度限制的数字)提供的最低级语言是 bc。在 bc 中,执行整个需求并不困难:

x=12345
y=2
scale=0;
s=0;
while (x>0) {
              b=x%10;
          x/=10;
          s+=b^y
        };
s
quit

将其写入文件(假设它被称为digpower.bc)并执行以下命令:

$ bc -q digpower.bc
55

将整个文件变形为仅包含一个处理时间变量并将比例返回到其原始值的函数:

define d(x,y){
    auto s,b,u; s=scale;
    while (x>0) { b=x%10; x/=10; u+=b^y }
    scale=s; return(u) };

在这种情况下,请像这样调用 bc:

$ bc -q digpower.bc <<<"d(12345,2)"
55

$ echo "d(3706907995955475988644381,25)" | bc digpower.bc
3706907995955475988644381

Python

下一个具有无限(仅通过计算机内存)整数的高级语言(跳过 Common lisp)是 Python:

$ python3 -c 'x=12345;y=2;s=0;
while (x>0): b=x%10;x//=10;s+=b**y                        
print(s)'
55

大多数其他语言使用某种形式的浮点数,包括awk.有些语言允许设置浮点数的大小(稍后 awk 中将详细介绍这一点)。

awk

里面的所有数字awk都存储为浮点数。默认情况下,awk 使用 53 位尾数。 53 位尾数的一般限制为 16 位。在某些特定的浮点数中,17 位甚至 18 位可能是准确的。

$ awk -vx=12345 -vy=2 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b^y}; print(s)}'
55

但是(具有其他值的相同代码):

$ awk -vx=3706907995955475988644381 -vy=25 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b^y}; print(s)}'
2989038141653481752100864

这显然是错误的,因为浮点数的内部表示在 16 位数字之后就完全错误了。只是为了展示它:

$ awk -vx=3706907995955475988644381 'BEGIN{print(x);print(x+0)}'
3706907995955475988644381
3706907995955475754516480

这些错误会导致更大的错误。

如果 awk 编译了 bignum (awk --version 的输出包含“GNU MPFR”),则 GNU awk 的替代方案是增加浮点尾数的位数:

$ awk -M -v PREC=100 -vx=3706907995955475988644381 -vy=25 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b**y}; print(s)}'
3706907995955475988644381

25 位十进制数字需要大约25*log(2)/log(10)二进制数字或 83 位尾数(确切的答案需要更长的时间来解释)。使用 100 可以提供足够的余量。

相关内容