Addition of extremely large numbers in shell script
Solution 1
Assuming they are decimal numbers, You could do:
paste -d + a.txt b.txt | bc
Beware that bc
line-wraps very long numbers (more than 68 or 69 digits depending on the implementation). With GNU bc
, you can disable it by setting the BC_LINE_LENGTH
environment variable to 0, like with:
paste -d + a.txt b.txt | BC_LINE_LENGTH=0 bc
Solution 2
The trick is to not use bash
to perform the addition1.
First, read each number into a separate variable. This assumes that the files contain only a number and no other information.
read a <a.txt
read b <b.txt
Then use the bc
calculator to get the result:
bc <<<"$a + $b"
bc
is an "arbitrary-precision arithmetic language and calculator".
To store the result in a variable c
:
c="$( bc <<<"$a + $b" )"
If the <<<
syntax feels weird (it's called a "here-string" and is an extension to the POSIX shell syntax supported by bash
and a few other shells), you may instead use printf
to send the addition to bc
:
printf '%s + %s\n' "$a" "$b" | bc
And storing the result in c
again:
c="$( printf '%s + %s\n' "$a" "$b" | bc )"
1 Using bash
to perform the addition of two extremely large numbers would require the implementation, in the bash
script, of a routine for doing arbitrary-precision arithmetic. This is perfectly doable, but cumbersome and unnecessary since every Unix comes with bc
, which already provides this service to you in a relatively easy and accessible way.
Solution 3
As both Stéphane and Kusalananda said, "really, just use bc", but if you really want to use bash for addition, here's a starting point (positive integers only) -- I'll leave it as an exercise for the reader to implement decimals and negative numbers:
function arbadd {
addend1=$1
addend2=$2
sum=
bcsum=$(echo $addend1 + $addend2 | BC_LINE_LENGTH=0 bc)
# zero-pad the smallest number
while [ ${#addend1} -lt ${#addend2} ]
do
addend1=0${addend1}
done
while [ ${#addend2} -lt ${#addend1} ]
do
addend2=0${addend2}
done
carry=0
for((index=${#addend1}-1;index >= 0; index--))
do
case ${carry}${addend1:index:1}${addend2:index:1} in
(000) carry=0; sum=0${sum};;
(001|010|100) carry=0; sum=1${sum};;
(002|011|020|101|110) carry=0; sum=2${sum};;
(003|012|021|030|102|111|120) carry=0; sum=3${sum};;
(004|013|022|031|040|103|112|121|130) carry=0; sum=4${sum};;
(005|014|023|032|041|050|104|113|122|131|140) carry=0; sum=5${sum};;
(006|015|024|033|042|051|060|105|114|123|132|141|150) carry=0; sum=6${sum};;
(007|016|025|034|043|052|061|070|106|115|124|133|142|151|160) carry=0; sum=7${sum};;
(008|017|026|035|044|053|062|071|080|107|116|125|134|143|152|161|170) carry=0; sum=8${sum};;
(009|018|027|036|045|054|063|072|081|090|108|117|126|135|144|153|162|171|180) carry=0; sum=9${sum};;
(019|028|037|046|055|064|073|082|091|109|118|127|136|145|154|163|172|181|190) carry=1; sum=0${sum};;
(029|038|047|056|065|074|083|092|119|128|137|146|155|164|173|182|191) carry=1; sum=1${sum};;
(039|048|057|066|075|084|093|129|138|147|156|165|174|183|192) carry=1; sum=2${sum};;
(049|058|067|076|085|094|139|148|157|166|175|184|193) carry=1; sum=3${sum};;
(059|068|077|086|095|149|158|167|176|185|194) carry=1; sum=4${sum};;
(069|078|087|096|159|168|177|186|195) carry=1; sum=5${sum};;
(079|088|097|169|178|187|196) carry=1; sum=6${sum};;
(089|098|179|188|197) carry=1; sum=7${sum};;
(099|189|198) carry=1; sum=8${sum};;
(199) carry=1; sum=9${sum};;
esac
done
if [ $carry -eq 1 ]
then
sum=1${sum}
fi
printf "Sum = %s\n" "$sum"
}
I've left the bc
comparison in there, but commented out, for comparison.
Related videos on Youtube
voldemort619
Updated on September 18, 2022Comments
-
voldemort619 almost 2 years
Suppose that two numbers are stored in two different files,
a.txt
andb.txt
.Each number is large enough (more than 30 digits) to not be supported by the numeric data type used by
bash
.How may I add them in the shell?
-
phk over 7 yearsPersonally I would use
python
or similar in that case. -
Jeff Schaller over 7 yearsSure you don't want to use sed for addition instead?
-
Sergiy Kolodyazhnyy over 7 yearsSome time ago, in my java class we used stacks for adding numbers that were out of java's max int range. Assuming you're willing to go to the trouble of implementing stack using arrays in bash , you can do that . . . but it's very redundant . . . and unnecessary as you can see from the answers below. Or just use
python
as phk suggested
-
-
Stéphane Chazelas over 7 yearsAlternatively, you could do
read a < a.txt
. That would also take care of stripping leading and trailing blanks if any (assuming$IFS
has not been modified). -
Bryce Guinta over 7 yearsWhy do the quotes inside the quotes not need to be escaped for the here-string inside the process substitution?
-
Kusalananda over 7 years@BryceGuinta Because unlike something like
echo "\"hello\""
, the thing within the$(...)
is not a string passed as an argument to another program, and the shell knows how to deal with the nesting of quotes. This is also why using$(...)
rather than backticks is better; you can write$( ... $( ... ) )
without any ambiguity, whereas the same thing using backticks is... awkward. -
voldemort619 over 7 yearsbut how to do in bash not using bc
-
Kusalananda over 7 years@voldemort619 You would have to implement additiion in a similar way as any one of these libraries do. You could have a look at this StackOverflow answer for an explanation. But really, just use
bc
.