
私は最初の数字の各桁の合計を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
シェルの演算では、bash
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
ほとんどのシェル実装の能力を超えていることは確かです。実際、シェルの算術演算は 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 を使用すると十分な余裕が得られます。