До нашей эры

До нашей эры

Я пытаюсь написать скрипт (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 дает достаточный запас.

Связанный контент