西元前

西元前

我正在嘗試編寫一個腳本(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 可以提供足夠的餘裕。

相關內容