
Я пытаюсь написать скрипт (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 двоичными цифрами (другой бит определяет знак целого числа), поэтому 63 единицы в двоичном формате или 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-десятичных цифр требуется около 25*log(2)/log(10)
двоичных цифр или 83-битная мантисса (точный ответ объяснить гораздо дольше). Использование 100 дает достаточный запас.