紀元前

紀元前

私は最初の数字の各桁の合計を2番目の数字の累乗で返すスクリプト(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

シェルの演算では、bashC コンパイラでサポートされている最も広い整数型が使用されます。ほとんどの最新のシステム/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ほとんどのシェル実装の能力を超えていることは確かです。実際、シェルの算術演算は C 言語の算術演算と非常によく似ています。C 言語では、符号付き整数はオーバーフローがあると符号が反転します。64 ビット システムで使用される一般的な最長のシステム整数の値は 63 の 2 進数に制限されています (もう 1 つのビットは整数の符号を定義します)。したがって、2 進数で 63 個の 1、または 16 進数で 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

パイソン

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

これは明らかに間違っています。なぜなら、float としての内部表現では 16 桁目以降がまったく間違っているからです。それを示すために:

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

これらのエラーはより大きなエラーにつながります。

GNU awk では、awk に bignum がコンパイルされている場合 (awk --version の出力に "GNU MPFR" が含まれる場合)、浮動小数点仮数のビット数を増やすという代替手段があります。

$ 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 桁の 10 進数には、25*log(2)/log(10)約 2 進数または 83 ビットの仮数が必要です (正確な答えを説明するには、かなり時間がかかります)。100 を使用すると十分な余裕が得られます。

関連情報