
Estou tentando escrever um script (script1.sh) que forneça a soma de cada dígito do primeiro número, elevado à potência do segundo número. Então
./script1.sh 12345 2
deve produzir55
(porque 1+4+9+16+25=55)
ou./script1.sh 3706907995955475988644381 25
deve produzir 3706907995955475988644381
.
Escrevi um script, mas em alguns casos recebo um resultado negativo e não vejo como isso pode acontecer.
Por exemplo
./script1.sh 3706907995955475988644380 25
saídas
-2119144605827694052
Meu roteiro:
#!/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
Responder1
A aritmética do shell bash
usa o tipo inteiro mais amplo suportado pelo seu compilador C. Na maioria dos sistemas modernos/compiladores C, são números inteiros de 64 bits, portanto, "apenas" cobrindo o intervalo -9223372036854775808 a 9223372036854775807 e agrupando números fora disso. Para fazer isso você precisará usar outra ferramenta, como 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"
Responder2
Com curtoawk
roteiro:
sum_powered.awk
roteiro:
#!/bin/awk -f
BEGIN{
split(ARGV[1], a, "");
pow = ARGV[2];
for (i in a) sum += a[i] ** pow;
print sum;
}
Uso:
$ ./sum_powered.awk 12345 2
55
$ ./sum_powered.awk 3706907995955475988644381 25
3706907995955475217645568
Talvez seja necessário adicionar permissão de execução a um novoestranhoscript antes de executar:
$ chmod +x sum_powered.awk
Responder3
Fazer contas com resultados de 25 dígitos ( 3706907995955475988644381
) certamente está fora da capacidade da maioria das implementações de shell. Na verdade, a aritmética do shell é bastante semelhante à aritmética da linguagem C. Na linguagem C, um número inteiro assinado muda de sinal quando há estouro. O inteiro de sistema mais longo comum para um sistema de 64 bits usaria tem um valor limitado a 63 dígitos binários (o outro bit define o sinal do inteiro), então, 63 1's em binário ou 0efff ffff ffff ffff em hexadecimal representa o número $( ( (1<<63) - 1 )) ou 9223372036854775807 (19 dígitos). O limite negativo é -9223372036854775808.
Um número de 25 dígitos não cabe em um número inteiro de 19 dígitos, então ele transborda, comoEstouro de inteiros assinados em C: alterando o sinal (na maioria das máquinas):
$ echo "$(( 9223372036854775807 + 1 ))"
-9223372036854775808
a.C.
A linguagem de nível mais baixo que um utilitário (autônomo) poderia fornecer para "matemática de precisão arbitrária" (números sem limite de comprimento predefinido) é bc. No bc, não é difícil executar todo o requisito:
x=12345
y=2
scale=0;
s=0;
while (x>0) {
b=x%10;
x/=10;
s+=b^y
};
s
quit
Escreva isso em um arquivo (suponha que seja chamado digpower.bc
) e execute com isto:
$ bc -q digpower.bc
55
Transformando todo o arquivo para conter apenas uma função que cuida das variáveis temporais e retornando a escala ao seu valor original:
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) };
Neste caso, chame bc assim:
$ bc -q digpower.bc <<<"d(12345,2)"
55
$ echo "d(3706907995955475988644381,25)" | bc digpower.bc
3706907995955475988644381
Pitão
A próxima linguagem de nível superior (ignorando Common lisp) que possui números inteiros ilimitados (apenas pela memória do computador) é Python:
$ python3 -c 'x=12345;y=2;s=0;
while (x>0): b=x%10;x//=10;s+=b**y
print(s)'
55
A maioria das outras linguagens usa alguma forma de floats, incluindo awk
. Algumas linguagens permitem definir o tamanho dos carros alegóricos (mais sobre isso no awk posteriormente).
estranho
Todos os números internos awk
são armazenados como números flutuantes. Por padrão, o awk usa mantissa de 53 bits. Uma mantissa de 53 bits tem um limite geral de 16 dígitos. Em alguns carros alegóricos específicos, 17 ou até 18 dígitos podem ser exatos.
$ awk -vx=12345 -vy=2 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b^y}; print(s)}'
55
Mas (o mesmo código com outros valores):
$ awk -vx=3706907995955475988644381 -vy=25 'BEGIN{s=0;while(x>0){b=x%10;x=int(x/10);s+=b^y}; print(s)}'
2989038141653481752100864
O que é obviamente errado porque a representação interna como float fica bastante errada após os 16 dígitos. Só para mostrar:
$ awk -vx=3706907995955475988644381 'BEGIN{print(x);print(x+0)}'
3706907995955475988644381
3706907995955475754516480
Esses erros levam a erros maiores.
Existe a alternativa com GNU awk de aumentar o número de bits da mantissa flutuante se o awk tiver bignum compilado (a saída de awk --version contém "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
Um dígito decimal de 25 requer cerca 25*log(2)/log(10)
de dígitos binários ou uma mantissa de 83 bits (a resposta exata é bem mais longa para explicar). Usar 100 dá margem suficiente.